【BZOJ5303】[HAOI2018]反色游戏(Tarjan,线性基)

【BZOJ5303】[HAOI2018]反色游戏(Tarjan,线性基)

题面

BZOJ
洛谷

题解

把所有点全部看成一个\(01\)串,那么每次选择一条边意味着在这个\(01\)串的基础上异或上一个有\(2\)\(1\)\(01\)串。
那么把边构建线性基,最终的答案显然就是\(2\)的不在线性基里的边数次方。
显然每次只需要考虑一个联通块,一个联通块随便拉出一棵生成树,就可以在线性基上确定\(n-1\)个元,那么对于其他边任意的情况,显然可以通过修改这\(n-1\)条边的选择情况使得最终满足条件,因此此时方案数是\(2^{m-n+1}\)。考虑如果一个联通块有解,那么黑点的个数必定为偶数个,这样子可以任意两两配对之后取反路径上的所有边,否则为奇数个无法配对,必定无解。
那么\(Tarjan\)做一遍计算一下需要的信息就好了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
#define MOD 1000000007
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1,dg[MAX];
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;++dg[u];}
int n,m,a[MAX],bin[MAX<<1];char ch[MAX];
int dfn[MAX],low[MAX],vis[MAX],tim,rt;
int sum[MAX],c[MAX],d[MAX],f[MAX];
void init()
{
	cnt=1;tim=0;
	for(int i=1;i<=n;++i)h[i]=dfn[i]=low[i]=dg[i]=d[i]=c[i]=f[i]=0;
}
void Tarjan(int u)
{
	dfn[u]=low[u]=++tim;vis[u]=rt;sum[u]=a[u];
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			Tarjan(v);sum[u]+=sum[v];
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u])d[u]|=(sum[v]&1),c[u]+=1,f[u]+=sum[v];
		}
		else low[u]=min(low[u],dfn[v]);
	}
	if(u==rt)c[u]-=1;
}
int main()
{
	bin[0]=1;for(int i=1;i<MAX<<1;++i)bin[i]=(bin[i-1]<<1)%MOD;
	int T=read();
	while(T--)
	{
		n=read();m=read();init();int val=0,tot=0;
		for(int i=1,u,v;i<=m;++i)u=read(),v=read(),Add(u,v),Add(v,u);
		scanf("%s",ch+1);for(int i=1;i<=n;++i)a[i]=ch[i]-48;
		for(int i=1;i<=n;++i)if(!dfn[i])rt=i,Tarjan(i),++tot,val+=sum[i]&1;
		printf("%d ",val?0:bin[m-n+tot]);
		for(int i=1;i<=n;++i)
			if(d[i])printf("0 ");
			else if(val-(sum[vis[i]]&1))printf("0 ");
			else if((sum[vis[i]]-a[i]-f[i])&1)printf("0 ");
			else printf("%d ",bin[m-n+tot-dg[i]+1+c[i]]);
		puts("");
	}
	return 0;
}
posted @ 2019-02-20 20:50  小蒟蒻yyb  阅读(391)  评论(0)    收藏  举报