【BZOJ5303】【HAOI2018】—反色游戏(结论+tarjan割点)

传送门

出题人的题解:

有个显然的结论,只有一个联通块中有偶数个节点才有解
为什么呢?我们将黑点任意两两配对,对一对黑点考虑它们之间的任意一条路径,将路径上的边的状态(操作or不操作)取反,立即可以得到一种合法方案,

那显然在一棵树上的方案数只有1种
考虑一个联通块的非树边,显然是可以替换掉树上的一条路径的
那非树边的选或不选就对应了不同的点集,则方案数就是2mn+12^{m-n+1}
而对于一个图,方案数就是2mn+p,p2^{m-n+p},p是联通块个数

那对于删去一个点就很简单了,考虑一下删去对所在联通块和黑点数目的影响分类讨论就可以了

注意特判一个点的情况

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=100005;
const int mod=1000000007;
int n,m,adj[N],nxt[N<<1],to[N<<1],in[N<<1],pow2[N],cnt;
int dfn[N],low[N],tot,cut[N],siz[N],vis[N],bel[N],belnum,sub[N];
char s[N];
inline void addedge(int u,int v){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
inline void clear(){
	memset(adj,0,sizeof(adj));
	memset(dfn,0,sizeof(dfn));
	memset(siz,0,sizeof(siz));
	memset(sub,0,sizeof(sub));
	memset(cut,0,sizeof(cut));
	memset(in,0,sizeof(in));
	cnt=tot=0;
}
void tarjan(int u,int fa){
	low[u]=dfn[u]=++tot;
	vis[u]=1,siz[u]=s[u]=='1';
	bel[u]=belnum;
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(!dfn[v]){
			tarjan(v,u);
			siz[u]+=siz[v];
			if(low[v]>=dfn[u]){
				++cut[u],vis[u]&=siz[v]%2==0;
				sub[u]+=siz[v];
			}
			else low[u]=min(low[u],low[v]);
		}
		else if(v!=fa){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(!fa)--cut[u];
}
int main(){
	pow2[0]=1;
	for(int i=1;i<N;++i)pow2[i]=pow2[i-1]*2%mod;
	int T=read();
	while(T--){
		n=read(),m=read();
		clear();
		for(int i=1;i<=m;++i){
			int u=read(),v=read();
			addedge(u,v),addedge(v,u);
			++in[u],++in[v];
		}
		scanf("%s",s+1);
		int num=0,odd=0;
		for(int i=1;i<=n;++i){
			if(!dfn[i]){
				belnum=i,tarjan(i,0);
				++num,odd+=siz[i]&1;
			}
		}
		int ans=m-n+num;
		cout<<((odd)?(0):(pow2[ans]));
		for(int i=1;i<=n;++i){
			if(!in[i])cout<<" "<<((odd==siz[i])?(pow2[ans]):(0));
			else{
				if(vis[i]&&((siz[bel[i]]-(s[i]=='1')-sub[i])%2==0)&&(odd==(siz[bel[i]]&1)))
					cout<<" "<<(pow2[ans-in[i]+1+cut[i]]);
				else cout<<" "<<0;
			}
		}
		puts("");
	}
}
posted @ 2019-02-28 14:34  Stargazer_cykoi  阅读(128)  评论(0编辑  收藏  举报