BZOJ3569-DZY Loves Chinese II
BZOJ3569-DZY Loves Chinese II
题意:
题解:
好神的题啊...
先求出原图的任意一棵生成树,然后图中的边就分为了生成树中的边和非生成树中的边.对于非生成树中的边,你给它rand一个权值,对于生成树中的边, 它的权值就是跨过它的非生成树中边的权值的xor和.然后对于每个询问,如果在给出的边中选出一些边是它们的权值xor为0,那么就图就不连通,否则连通.
该做法正确性证明见文末链接.
对于求生成树中的边的权值可以用差分求.对于求一个集合是否有xor为0的子集,我好像瞎搞了一个线性基做法,就是把一个数加入线性基后变成了0,那就说明有xor为0的子集.
#include<cstdio> #include<vector> #include<cstdlib> using namespace std; typedef long long ll; const int maxn=100000,maxm=500000,lgn=18,lgv=62; int n,m,fa[maxn+10],dep[maxn+10]; ll c[maxn+10],d[lgv+1],q,pre; vector<int> G[maxn+10]; int getf(int x){return fa[x]==x?x:fa[x]=getf(fa[x]);} struct edge{ int u,v,w; bool type; ll value; }eg[maxm+10]; void dfs(int x,int fa){ dep[x]=dep[fa]+1; for(int i=0;i<G[x].size();++i){ int e=G[x][i]; if(e!=fa){ dfs(e,x); c[x]^=c[e]; } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) fa[i]=i; for(int i=1;i<=m;++i){ scanf("%d%d",&eg[i].u,&eg[i].v); if(getf(eg[i].u)==getf(eg[i].v)){ eg[i].type=1; eg[i].value=1ll*rand()*rand()*rand()*rand(); c[eg[i].u]^=eg[i].value; c[eg[i].v]^=eg[i].value; }else{ fa[getf(eg[i].u)]=getf(eg[i].v); G[eg[i].u].push_back(eg[i].v); G[eg[i].v].push_back(eg[i].u); } } dfs(1,0); for(int i=1;i<=m;++i) if(!eg[i].type) if(dep[eg[i].u]>dep[eg[i].v]) eg[i].value=c[eg[i].u]; else eg[i].value=c[eg[i].v]; scanf("%d",&q); for(;q--;){ int k,x; scanf("%d",&k); bool suc=0; for(int i=0;i<=lgv;++i) d[i]=0; for(;k--;){ scanf("%d",&x); x^=pre; ll now=eg[x].value; for(int i=lgv;i>=0;--i) if(now>>i&1) if(!d[i]){ d[i]=now; break; }else now^=d[i]; if(!now) suc=1; } printf("%s\n",suc?"Disconnected":"Connected"); pre+=!suc; } return 0; }
update:刚刚在uoj上看见了一个非常妙的证明:http://wuhongxun.blog.uoj.ac/blog/3003.