题意:在一个无向图上每次删去少量边,问是否还连通?
标程:

1 #include<bits/stdc++.h> 2 using namespace std; 3 int read() 4 { 5 int x=0;char ch=getchar(); 6 while (ch<'0'||ch>'9') ch=getchar(); 7 while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 8 return x; 9 } 10 const int N=200005; 11 int cnt,head[N],n,m,c,k,w,val[N],u[N],v[N],sum,fl,vis[N],base[32],fa[N]; 12 map<int,int> mp; 13 struct node{int to,next,w;}num[N*2]; 14 void add(int x,int y,int w) 15 {num[++cnt].to=y;num[cnt].next=head[x];num[cnt].w=w;head[x]=cnt;} 16 int ins(int x) 17 { 18 for (int i=30;i>=0;i--) 19 if ((x>>i)&1) 20 if (!base[i]) return base[i]=x,1; 21 else x^=base[i]; 22 return 0; 23 } 24 void dfs(int x) 25 { 26 vis[x]=1; 27 for (int i=head[x];i;i=num[i].next) 28 { 29 if (!vis[num[i].to]) fa[num[i].to]=x,dfs(num[i].to),val[x]^=val[num[i].to]; 30 else if (num[i].to!=fa[x]) val[x]^=num[i].w; 31 } 32 } 33 int main() 34 { 35 srand(2333); 36 n=read(),m=read(); 37 for (int i=1;i<=m;i++) u[i]=read(),v[i]=read(),w=rand()*131,add(u[i],v[i],w),add(v[i],u[i],w); 38 dfs(1); k=read(); 39 for (int i=1;i<=n;i++) 40 if (!vis[i]) { 41 for (int j=1;j<=k;j++) puts("Disconnected"); 42 return 0; 43 } 44 while (k--) 45 { 46 c=read();memset(base,0,sizeof(base));fl=1; 47 for (int i=1;i<=c;i++) 48 { 49 int x=read(); 50 if (fa[u[x]]==v[x]) fl&=ins(val[u[x]]); 51 else if(fa[v[x]]==u[x]) fl&=ins(val[v[x]]); 52 else fl&=ins(num[x*2].w); 53 } 54 puts(fl?"Connected":"Disconnected"); 55 } 56 return 0; 57 }
题解:随机权值+断开生成子树+线性基/cdq分治
对每一条边随机一个权值。建一棵生成树,如果要切去一个子树的话必须把子树连到其父亲的根切掉,以及子树中所有的返祖边。
对于每一个节点保存子树中所有返祖边的异或和。删去的边,如果是非树边,就把其权值扔进线性基;是树边,扔进其保存的子树异或和。
那么如果某个时刻某权值已经可以在线性基中被线性表示,那么说明Disconnected。
随机权值这个东西可以用来处理序列上出现奇数次的元素是否恰好有0/1/2个,某条路径是否被全部经过,图联通的判定等等。
cdq分治