BZOJ5303: [Haoi2018]反色游戏

BZOJ5303: [Haoi2018]反色游戏

https://lydsy.com/JudgeOnline/problem.php?id=5303

分析:

  • 如果\(1\)的个数为奇数,肯定无解。
  • 否则,如果是一棵树,答案为\(1\)
  • 否则,多出的那些边不会影响答案,答案为\(2^{n-m-1}\)
  • 若干个连通块需要乘起来,设连通块有\(tot\)个,那么答案就是\(2^{n-m-tot}\)
  • 分析删点后的影响。
    1. 孤立点,由于会被当做非割点需要单独拿出来特判。
    1. 非割点,没啥可说的,直接用公式计算即可。
    1. 割点,由于删去后需要重新计算连通块数和每个连通块内\(1\)的个数,我这里用圆方树来处理每个割点,在圆方树上可能会比较好理解,在原图上删掉这个点和在圆方树上删掉这个点是等价的。
  • 具体实现看代码,代码写的比较恶心。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
using namespace std;
#define N 200050
#define mod 1000000007
#define mem(x) memset(x,0,sizeof(x))
typedef long long ll;
vector<int>V[N];
int head[N],to[N<<1],nxt[N<<1],cnt,n,m;
int dfn[N],low[N],S[N<<1],tp,vis[N<<1],bl[N],iscut[N],du[N],bcc,fa[N],tot,siz[N],se[N],sw[N];
char w[N];
ll totans,tans[N],ans[N],mi[N];
ll qp(ll x,ll y) {
	ll re=1;for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod; return re;
}
inline void add(int u,int v) {
	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void tarjan(int x,int rt) {
	int i,num=0;
	fa[x]=tot;
	siz[tot]++;
	sw[tot]+=(w[x]=='1');
	dfn[x]=low[x]=++dfn[0];
	for(i=head[x];i;i=nxt[i]) if(!vis[i]) {
		S[++tp]=i;
		num++;
		se[tot]++;
		vis[i]=vis[i^1]=1;
		if(!dfn[to[i]]) {
			tarjan(to[i],x);
			low[x]=min(low[x],low[to[i]]);
			if(low[to[i]]>=dfn[x]) {
				bcc++; V[bcc].clear();
				int t=0;
				iscut[x]=1;
				while(t!=i) {
					t=S[tp--];
					int u=to[t], v=to[t^1];
					if(bl[u]!=bcc) bl[u]=bcc,V[bcc].push_back(u);
					if(bl[v]!=bcc) bl[v]=bcc,V[bcc].push_back(v);
				}
			}
		}else low[x]=min(low[x],dfn[to[i]]);
	}
	if(x==rt&&num<2) iscut[rt]=0;
}
int hme,flg[N],TOT;
void dfs(int x,int y) {
	int i,cc=0; vis[x]=1;
	if(x<=n) siz[x]=w[x]=='1';
	else siz[x]=0;
	for(i=head[x];i;i=nxt[i]) if(to[i]!=y) {
		dfs(to[i],x);
		siz[x]+=siz[to[i]];
		if(siz[to[i]]&1) {
			flg[x]=1;
		}
		cc++;
	}
	if(x>n) return ;
	if(y) cc++;
	if((sw[fa[x]]-siz[x])&1) flg[x]=1;
	if(hme-(sw[fa[x]]&1)>0) flg[x]=1;
	if(iscut[x]) {
		if(flg[x]) ans[x]=0;
		else {
			ans[x]=mi[m-du[x]-n+1+cc+tot-1];
		}
	}
}
void solve() {
	memset(head,0,sizeof(head)); cnt=1;
	memset(dfn,0,sizeof(dfn));
	memset(vis,0,sizeof(vis));
	memset(bl,0,sizeof(bl));
	memset(iscut,0,sizeof(iscut));
	bcc=0;
	memset(siz,0,sizeof(siz));
	memset(se,0,sizeof(se));
	memset(sw,0,sizeof(sw));
	memset(du,0,sizeof(du));
	memset(flg,0,sizeof(flg));
	mem(fa);
	TOT=0; tot=0; tp=0; hme=0;
	scanf("%d%d",&n,&m);
	int i,x,y,j;
	int lm=max(n,m);
	for(mi[0]=i=1;i<=lm;i++) mi[i]=mi[i-1]*2%mod;
	for(i=1;i<=m;i++) {
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x); du[x]++; du[y]++;
	}
	scanf("%s",w+1);
	for(i=1;i<=n;i++) if(!dfn[i]) {
		tot++; tarjan(i,i);
	}
	totans=1;
	for(i=1;i<=tot;i++) {
		if(sw[i]&1) tans[i]=0,hme++;
		else {
			tans[i]=mi[se[i]-siz[i]+1];
			totans=totans*tans[i]%mod;
		}
	}
	if(hme) ans[0]=0;
	else ans[0]=totans;
	for(x=1;x<=n;x++) {
		if(siz[fa[x]]==1) {
			if(hme-(sw[fa[x]]&1)>0) ans[x]=0;
			else ans[x]=totans;
			continue;
		}
		if(!iscut[x]) {
			if(hme-(sw[fa[x]]&1)>0) ans[x]=0;
			else if((sw[fa[x]]-(w[x]=='1'))&1) ans[x]=0;
			else ans[x]=mi[m-du[x]-n+1+tot];
		}
	}
	memset(head,0,sizeof(head)); cnt=0;
	for(i=1;i<=bcc;i++) {
		int lim=V[i].size();
		for(j=0;j<lim;j++) {
			add(V[i][j],i+n);
			add(i+n,V[i][j]);
		}
	}
	memset(siz,0,sizeof(siz));
	mem(vis);
	int ln=n+bcc;
	for(i=1;i<=n;i++) TOT+=w[i]=='1';
	for(i=1;i<=ln;i++) if(!vis[i]) {
		dfs(i,0);
	}
	for(i=0;i<=n;i++) printf("%lld ",ans[i]); puts("");
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) solve();
}
posted @ 2019-01-06 20:16  fcwww  阅读(246)  评论(0编辑  收藏  举报