序列(sequence)
不妨把所有的 YES
当且仅当所有连通块都有解。下面记点
而
考虑新图的一条路径
而对于不包含奇环的连通块,它一定是一个二分图。将其黑白染色,那么黑点到黑点、白点到白点的权值都可以互相转运,我们将黑点集和白点集分别缩点。黑点与白点之间只有长度为奇数的路径,即可以将黑点集的权值和 与 白点集的权值和同时加上一个值。那么我们现在已经可以得出这个连通块有解的条件了:
- 权值和为偶数;
- 若该连通块中所有点的
tag 都为0 ,则黑点和白点的b_i 之和相等。
证明比较显然。第二次的“缩点”不需要真缩,DFS 判断一下就可以了。
于是这道题就做完了,复杂度
代码:
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int MAXN=110000;
struct Edge
{
int u, v;
Edge() {}
Edge(int a, int b): u(a), v(b) {}
} e[MAXN];
vector<int> g[MAXN], h[MAXN];
int a[MAXN];
int bel[MAXN], bw[MAXN];
ll sum[MAXN];
bool tag[MAXN];
void dfs1(int u, int c)
{
bel[u]=c, sum[c]+=a[u];
for (int i=0; i<g[u].size(); i++)
{
int v=g[u][i];
if (!bel[v]) dfs1(v, c);
}
}
bool dfs2(int u, int c, bool& t, ll& sb, ll& sw)
{
if (~bw[u]) return bw[u]==c;
bw[u]=c, t|=tag[u];
if (c==0) sb+=sum[u];
else sw+=sum[u];
bool ret=1;
for (int i=0; i<h[u].size(); i++)
{
int v=h[u][i];
ret&=dfs2(v, c^1, t, sb, sw);
}
return ret;
}
int main()
{
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
int T;
scanf("%d", &T);
while (T--)
{
int n, m, p=0, k=0;
scanf("%d%d", &n, &m);
for (int i=1; i<=n; i++) scanf("%d", &a[i]);
for (int i=1; i<=n; i++)
{
int x; scanf("%d", &x); a[i]=x-a[i];
g[i].clear(), h[i].clear();
}
for (int i=1; i<=m; i++)
{
int t, u, v;
scanf("%d%d%d", &t, &u, &v);
if (t==1) e[++p]=Edge(u, v);
else g[u].push_back(v), g[v].push_back(u);
}
memset(bel, 0, sizeof bel);
memset(sum, 0, sizeof sum);
memset(tag, 0, sizeof tag);
for (int i=1; i<=n; i++)
if (!bel[i]) dfs1(i, ++k);
for (int i=1; i<=p; i++)
if (bel[e[i].u]==bel[e[i].v]) tag[bel[e[i].u]]=1;
else
{
h[bel[e[i].u]].push_back(bel[e[i].v]);
h[bel[e[i].v]].push_back(bel[e[i].u]);
}
memset(bw, -1, sizeof bw);
bool ans=1;
for (int i=1; i<=k; i++)
if (bw[i]==-1)
{
bool tg=0; ll sumb=0, sumw=0;
if (dfs2(i, 0, tg, sumb, sumw))
if (tg) ans&=(sumb+sumw)%2==0;
else ans&=sumb==sumw;
else ans&=(sumb+sumw)%2==0;
}
puts(ans?"YES":"NO");
}
return 0;
}
冒泡排序(bubble)
考虑每一轮冒泡排序会对序列产生什么影响。记
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN=220000;
int n, m;
template<typename T>
struct BIT
{
T s[MAXN];
static int lb(int x) { return x&-x; }
void clr() { for (int i=1; i<=n; i++) s[i]=0; }
void add(int x, int k) { while (x>0) s[x]+=k, x-=lb(x); }
T sum(int x) { T r=0; while (x<=n) r+=s[x], x+=lb(x); return r; }
};
int p[MAXN], c[MAXN];
BIT<int> bit1; BIT<ll> bit2;
void change(int x, int k)
{
bit1.add(c[x], k);
bit2.add(c[x], k*c[x]);
}
int main()
{
freopen("bubble.in", "r", stdin);
freopen("bubble.out", "w", stdout);
scanf("%d%d", &n, &m);
ll sum=0;
for (int i=1; i<=n; i++)
{
scanf("%d", &p[i]);
sum+=c[i]=bit1.sum(p[i]);
bit1.add(p[i], 1);
}
bit1.clr();
for (int i=1; i<=n; i++) change(i, 1);
while (m--)
{
int op, x;
scanf("%d%d", &op, &x);
if (op==1)
{
change(x, -1), change(x+1, -1);
swap(c[x], c[x+1]);
if (p[x]<p[x+1]) c[x+1]++, sum++;
else c[x]--, sum--;
change(x, 1), change(x+1, 1);
swap(p[x], p[x+1]);
}
else
{
if (x>n) puts("0");
else if (x==0) printf("%lld\n", sum);
else printf("%lld\n", bit2.sum(x)-1ll*x*bit1.sum(x));
}
}
return 0;
}
最小环(ring)
为了方便后面的操作,首先将
- 将序列拆成若干个模
k 的环,一个环中相邻元素距离为k ,不同环之间不影响; - 将
a_i 按顺序分给这些环,每个环中贪心地选最小的放在中间,然后在右边放次小的,左边放第三小的……以此类推。
进一步观察,发现对于
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int MAXN=220000;
int n, m, a[MAXN];
bool b[MAXN];
ll ans[MAXN];
ll calc(const vector<int>& c)
{
int l=c[0], r=0; ll ret=0;
for (int i=0; i<c.size(); i++)
if (i&1) ret+=1ll*l*c[i], l=c[i];
else ret+=1ll*r*c[i], r=c[i];
ret+=1ll*l*r;
return ret;
}
void work(int d)
{
memset(b, 0, sizeof b);
int k=0;
for (int i=0; i<n; i++)
if (!b[i])
{
vector<int> c;
int j=i;
do
{
c.push_back(a[k++]);
b[j]=1, j=(j+d)%n;
}
while (j!=i);
ans[d]+=calc(c);
}
}
int main()
{
freopen("ring.in", "r", stdin);
freopen("ring.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i=0; i<n; i++) scanf("%d", &a[i]);
sort(a, a+n);
for (int i=1; i*i<=n; i++)
if (n%i==0)
{
work(i);
if (i*i!=n) work(n/i);
}
while (m--)
{
int k; scanf("%d", &k);
printf("%lld\n", ans[__gcd(n, k)]);
}
return 0;
}