做法超好想,细节调一辈子。 估计这句话最适合这个题了hhhh。
首先一个很显然的想法:把边设成未知数,对点列异或方程,最后的解的个数就是 2^自由元 。
不过如果某个联通分量里有奇数个黑点,那么问题无解。然后我来证明一下:
把一个联通分量里所有点代表的方程都异或起来,因为这个联通分量里的边恰好在其两个端点的行是1,所以最后异或出来的方程前m列都是0,如果有奇数个黑点的话,最后一列就是1,那么无解(不能0=1吧hhh)。
直接高斯消元是 O(N^3) 的,肯定要gg了啊 (不过竟然给了60分消元hhh,出题人太良心了)。
不过一般这种题我们如果按照特定的顺序消(shou)元(wan)的话,会得到一些很良心的结论(具体参考 bzoj 文艺计算姬)。
考虑 边1 的两个端点 u,v,因为最后只能有一行对应在这列是1,所以我们就把第v行变成 第u行 异或 第v行,可以发现得到的新行的意义是:u点和v点合并后的新点(因为边1被消去了,正好对应一个点没有自环)。
当然,如果我们考虑边i的时候,发现它的两个端点已经在一个联通分量里了,那么这条边就是自由元,因为我们现在在高斯消元的矩阵里已经找不到第i列是1的行了。
我们把上述做法扩展一下,就可以得到一个能够算出全局答案的做法:初始答案是1,并查集维护连通性,如果尝试合并失败,那么答案*2。
但是这还不够棒,因为本题要求出 删去 每一个点之后的答案。
不过上述做法还是有点多余,因为我们仔细想一下它的过程,就可以发现其实答案就是 2^(m-n + 联通分量个数)。
所以,现在我们还剩下的最大的问题就是:如何维护,删去一个点之后,可能带来的某个联通分量的黑点变成奇数个(对应无解) 和 联通分量个数的变动 (对应图的连通性) 的影响???
跑个tarjan求割点的算法就好啦,第一个问题有些复杂,我们先解决一下第二个问题。
第二个问题无非可以分以下几种情况:
1.删去的节点是一个孤立顶点: 这种情况下 会使图中联通分量个数-1.
2.删去一个割点: 这种情况可以在tarjan的时候顺带统计,不过如果这个点是dfs树的根的话答案还要再-1(因为上面就没有新的联通分量了)
3.删去一个非孤立顶点且非割点:没有影响hhhh。
但是不要忘了,删一个点的时候不仅会对联通分量个数产生影响,还会对图的总边数-点数 产生影响。
至于第一个问题,我们可以在tarjan的时候处理出:删去一个点之后,图中 有奇数个黑点的联通分量 的个数的 变化量 derta[i]。
怎么处理呢?
首先如果这个点是割点的话:
1.如果这个点在的联通分量本来就有 奇数个黑点 的话,那么先让 derta[i]-- ,因为删去这个点会破坏这个联通分量。
2.再让derta[i]加上 删去这个点之后, 在dfs树上不能回到祖先的有奇数个黑点的联通分量个数 。
3.如果 dfs树根所在的联通分量再删去这个点之后也有奇数个黑点,那么 derta[i]++。
如果这个点i是非割点且非孤立顶点的话,那么derta[i] = color[i] == 1 ? (color[this tree] == 1 ? -1 : 1) : 0 .
如果这个点是孤立顶点的话,那么derta[i] = color[i] == 1 ? -1 : 0.
(呼。。。。。终于写完了累死我了。。。。)
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=200005,ha=1000000007; inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;} int to[maxn*2],ne[maxn*2],hd[maxn],num,n,m,T,ans,Sub[maxn]; int Xor[maxn],NewC[maxn],NewB[maxn],BC,ci[maxn]; int dfn[maxn],low[maxn],dc,tp,st[maxn],D[maxn]; bool iscut[maxn]; char S[maxn]; inline void addline(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num,D[x]++;} inline void init(){ memset(hd,0,sizeof(hd)),num=0; memset(NewB,0,sizeof(NewB)); memset(NewC,0,sizeof(NewC)); memset(D,0,sizeof(D)); memset(iscut,0,sizeof(iscut)); memset(dfn,0,sizeof(dfn)); memset(Sub,0,sizeof(Sub)); dc=BC=0; } inline int read(){ int x=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()); for(;isdigit(ch);ch=getchar()) x=x*10+ch-‘0‘; return x; } void W(int x){ if(x>=10) W(x/10); putchar(x%10+‘0‘);} void dfs(int x,int fa){ st[++tp]=x,dfn[x]=low[x]=++dc; int cd=0; Sub[x]=Xor[x]; for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa) if(!dfn[to[i]]){ cd++,dfs(to[i],x); Xor[x]^=Xor[to[i]]; low[x]=min(low[x],low[to[i]]); if(low[to[i]]>=dfn[x]){ Sub[x]^=Xor[to[i]]; iscut[x]=1,NewC[x]++; if(Xor[to[i]]) NewB[x]++; } } else low[x]=min(low[x],dfn[to[i]]); if(cd==1&&fa<0) iscut[x]=NewB[x]=NewC[x]=0; else if(fa<0) NewC[x]--; } inline void solve(){ for(int i=1;i<=n;i++) if(S[i]==‘1‘) Xor[i]=1; else Xor[i]=0; ans=m-n; for(int i=1;i<=n;i++) if(!dfn[i]){ ans++; dfs(i,-1); if(Xor[i]) BC++; if(!hd[i]) NewC[i]=-1; for(;tp;tp--) if(iscut[st[tp]]){ if(Xor[i]^Sub[st[tp]]) NewB[st[tp]]++; if(Xor[i]==1) NewB[st[tp]]--; } else NewB[st[tp]]=S[st[tp]]==‘1‘?(Xor[i]?-1:1):0; } if(BC) W(0); else W(ci[ans]); putchar(‘ ‘); for(int i=1;i<=n;i++){ BC+=NewB[i],ans+=NewC[i]-D[i]+1; if(!BC) W(ci[ans]); else W(0); putchar(‘ ‘); BC-=NewB[i],ans-=NewC[i]-D[i]+1; } puts(""); } int main(){ ci[0]=1; for(int i=1;i<=200000;i++) ci[i]=add(ci[i-1],ci[i-1]); T=read(); while(T--){ int uu,vv; init(),n=read(),m=read(); for(int i=1;i<=m;i++) uu=read(),vv=read(),addline(uu,vv),addline(vv,uu); scanf("%s",S+1),solve(); } return 0; }
原文地址:https://www.cnblogs.com/JYYHH/p/8970674.html