BZOJ5303: [Haoi2018]反色游戏
https://lydsy.com/JudgeOnline/problem.php?id=5303
分析:
- 如果(1)的个数为奇数,肯定无解。
- 否则,如果是一棵树,答案为(1)。
- 否则,多出的那些边不会影响答案,答案为(2^{n-m-1})。
- 若干个连通块需要乘起来,设连通块有(tot)个,那么答案就是(2^{n-m-tot})。
- 分析删点后的影响。
-
- 孤立点,由于会被当做非割点需要单独拿出来特判。
-
- 非割点,没啥可说的,直接用公式计算即可。
-
- 割点,由于删去后需要重新计算连通块数和每个连通块内(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();
}