bzoj 3569 DZY Loves Chinese II 随机算法 树上倍增
题意:给你一个n个点m条边的图,有若干组询问,每次询问会选择图中的一些边删除,删除之后问此图是否联通?询问之间相互独立。此题强制在线。
思路:首先对于这张图随便求一颗生成树,对于每一条非树边,随机一个权值。树边的权值为所有覆盖这条树边的非树边的权值异或和。覆盖是指这条边是个返祖边,并且一端在父节点方向,一端在子节点方向。这样,我们选出若干条边,看一下他们异或起来是不是0,如果是0,那么相当于把一条树边和它的所有子节点方向的返祖边全部断开,那么图就不连通了。
代码:
#include <bits/stdc++.h> #define LL long long #define pii pair<int, int> using namespace std; const int maxn = 100010; const int maxm = 500010; const LL mod = 1e18; mt19937 Random(time(0)); LL get(void) { return 1ll * Random() * rand(); } vector<pii> G[maxn], G1[maxn]; LL val[maxn]; int d[maxn], f[maxn][18], t; bool v[maxn], v1[maxm]; struct edge { int u, v; LL w; }; edge a[maxm]; void add(int x, int y, int id) { G[x].push_back(make_pair(y, id)); G[y].push_back(make_pair(x, id)); } void add1(int x, int y, int id) { G1[x].push_back(make_pair(y, id)); G1[y].push_back(make_pair(x, id)); } void dfs(int x) { v[x] = 1; for (auto y : G[x]) { if(v[y.first]) continue; dfs(y.first); add1(x, y.first, y.second); v1[y.second] = 1; } } queue<int> q; void bfs() { q.push(1); d[1] = 1; while(q.size()) { int x = q.front(); q.pop(); for (auto y : G1[x]) { if(d[y.first]) continue; d[y.first] = d[x] + 1; f[y.first][0] = x; for (int i = 1; i <= t; i++) { f[y.first][i] = f[f[y.first][i - 1]][i - 1]; } q.push(y.first); } } } int lca(int x, int y) { if(d[x] > d[y]) swap(d[x], d[y]); for (int i = t; i >= 0; i--) { if(d[f[y][i]] >= d[x]) y = f[y][i]; } if(x == y) return x; for (int i = t; i >= 0; i--) { if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; } return f[x][0]; } LL dfs1(int x, int fa) { for (auto y : G1[x]) { if(y.first == fa) continue; dfs1(y.first, x); a[y.second].w = val[y.first]; val[x] ^= val[y.first]; } } struct L_B{ long long d[61],p[61]; int cnt; void init(){ memset(d,0,sizeof(d)); memset(p,0,sizeof(p)); cnt=0; } bool insert(long long val){ for (int i=60;i>=0;i--) if (val&(1LL<<i)) { if (!d[i]) { d[i]=val; break; } val^=d[i]; } return val>0; } long long query_max() { long long ret=0; for (int i=60;i>=0;i--) if ((ret^d[i])>ret) ret^=d[i]; return ret; } long long query_min() { for (int i=0;i<=60;i++) if (d[i]) return d[i]; return 0; } void rebuild() { for (int i=60;i>=0;i--) for (int j=i-1;j>=0;j--) if (d[i]&(1LL<<j)) d[i]^=d[j]; for (int i=0;i<=60;i++) if (d[i]) p[cnt++]=d[i]; } long long kthquery(long long k) { int ret=0; if (k>=(1LL<<cnt)) return -1; for (int i=60;i>=0;i--) if (k&(1LL<<i)) ret^=p[i]; return ret; } }; int main() { int n, m, cnt = 0, x, y, num, T; srand(time(0)); scanf("%d%d", &n, &m); t = (int)(log(n) / log(2)) + 1; for (int i = 1; i <= m; i++) { scanf("%d%d", &a[i].u, &a[i].v); add(a[i].u, a[i].v, i); } dfs(1); bfs(); for (int i = 1; i <= m; i++) { if(v1[i]) continue; a[i].w = get(); int tmp = lca(a[i].u, a[i].v); val[a[i].u] ^= a[i].w; val[tmp] ^= a[i].w; val[a[i].v] ^= a[i].w; val[tmp] ^= a[i].w; } dfs1(1, -1); scanf("%d", &T); while(T--) { scanf("%d", &num); bool flag = 1; L_B solve; solve.init(); for (int i = 1; i <= num; i++) { scanf("%d", &x); x ^= cnt; flag = (flag & solve.insert(a[x].w)); } if(flag == 0) { printf("Disconnected\n"); } else { printf("Connected\n"); cnt++; } } }