[HAOI2018] 反色游戏

 

    做法超好想,细节调一辈子。 估计这句话最适合这个题了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;
}

  

posted @ 2018-04-29 11:30  蒟蒻JHY  阅读(1032)  评论(1编辑  收藏  举报