3263是道sb题,随便水过去吧。。。。。
3569才是正真的神题!
用dfs搞出一棵生成树,把边分为树边和非树边。
对于非树边我们随机分配一个数值给他,而树边上的值为覆盖其所有非树边权值的异或和。
我们来看一看删边的情况:
1、仅删除非树边,很明显这样一定仍是联通的。
2、删除树边和非树边,只有将树边和覆盖这条树边的非树边全部删除才能使图不联通,由于我们刚刚的预处理,这些边权值的异或和为0。
3、仅删除树边,这种情况比较复杂,但是我们讨论一下只删除两条树边的情况可以发现也只有删除的边权异或和为0时才会使图不联通。
有了这些我们就能把原问题转化为求删除的边集是否有存在一个子集其权值异或和为0。
rhl大神介绍了一种特别屌的方法:开一个64位的数组,初始为0。每一权值x从63位向0位扫描,如果此为x为1且数组内无值,那么把此为赋值为x,退出。如果x此位为1且数组内有值,那么x异或上这个值。如果某个边值最终能够扫描完整数组就说明存在我们要找的子集,图不联通。(证明似乎和线性相关与线性无关有关)
为了减少对第一种情况的误判,最好将权值设为一个64位unsigned long long,自己也可以随便(生日)设一个随机种子防止被卡。
//千万别作死开srand(time(0)),八中上re了三次。。。。。
#include <ctime> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int Maxn=100005; const int Maxm=1000005; typedef unsigned long long ULL; ULL g[Maxm],d[70],w[Maxm],tmp; int node[Maxm],next[Maxm],a[Maxm],fa[Maxm],v[Maxm]; int n,m,s,cnt,i,x,y,q,tot,tim; void Rand(ULL &x) { x = 1; for (int i=0;i<4;i++) x *= rand(); } void add(int x,int y){ node[++tot]=y; next[tot]=a[x]; a[x]=tot; node[++tot]=x; next[tot]=a[y]; a[y]=tot; } void dfs(int x){ v[x] = ++tim; for (int i=a[x];i;i=next[i]) if (fa[x]!=node[i]){ if (v[node[i]]!=0){ if (v[node[i]]>v[x]) continue; Rand( w[i/2] ); g[x] ^= w[i/2]; g[node[i]] ^= w[i/2]; } else { fa[node[i]] = x; dfs(node[i]); w[i/2] = g[node[i]]; g[x] ^= g[node[i]]; } } } bool Judge(){ for (int i=2;i<=n;i++) if (fa[i]==0) return 0; return 1; } int main(){ scanf("%d%d",&n,&m); for (i=1,tot=1;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y); } dfs(1); if (!Judge()){ scanf("%d",&q); while (q--) puts("Disconnected"); return 0; } cnt = 0; scanf("%d",&q); while (q--){ scanf("%d",&s); bool flag = 0; for (i=0;i<64;i++) d[i]=0; while (s--){ scanf("%d",&x); x ^= cnt; tmp = w[x]; for (i=63;i>=0;i--) if ( (tmp>>i)&1 ){ if (d[i]!=0) tmp ^= d[i]; else {d[i]=tmp;break;} } if (tmp==0) flag=1; } if (!flag) puts("Connected"), cnt++; else puts("Disconnected"); } return 0; }