【题目】
lydsy
给定一幅
n
n
n个点
m
m
m条边的无向图,初始每个节点有一个颜色。现在对每条边进行抉择:要么将其两端点反色,要么不变。问所有
2
m
2^m
2m种决策中有多少种决策使得所有点变白。另外还问将第
i
i
i个点删除后有又有多少种决策满足要求。
n
,
m
≤
1
0
5
n,m\leq 10^5
n,m≤105
【解题思路】
不妨先从树的情况开始考虑。不难发现,对于一个状态,若其所有黑色连通块都为偶数大小,那么我们只需要将每个黑色连通块内的点两两匹配,然后将路径上所有边进行反色就能满足要求(当然实际操作时只需要看一条边需要被操作奇数次还是偶数次)。那么实际上若这幅图是一棵树且所有黑色连通块大小都为偶数,则有且只有一种方案满足要求。
那么现在要拓展成图,不妨考虑每一条非树边对答案的贡献。若我们对一条非树边进行操作,那么我们只需要将这条非树边连接的两点在树上对应的路径进行操作即可以对应一种合法方案。不难得到,若一幅图所有黑色连通块都是偶数大小,那么一共有 2 m − n + c 2^{m-n+c} 2m−n+c种方案。其中 c c c是连通块个数。
现在要删去一个点后求答案,那么就是一个分类讨论:若这个点是黑点,则看看会不会使它所在的连通块变成偶数大小(拆开后)。若这个点是白点,就是求有多少个连通块,这个东西我们只需要先求个点双出来,然后看看这个点连接多少个点双即可。
复杂度 O ( n + m ) O(n+m) O(n+m)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,mod=1e9+7;
int n,m,ind,tot,ID,c,ans;
int head[N],low[N],dfn[N],bl[N];
int du[N],w[N],d[N],f[N],g[N],fc[N];
char s[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
void writesp(int x){write(x);putchar(' ');}
struct Tway{int v,nex;}e[N<<1];
void add(int u,int v)
{
++du[u];++du[v];
e[++tot]=(Tway){v,head[u]};head[u]=tot;
e[++tot]=(Tway){u,head[v]};head[v]=tot;
}
void tarjan(int x)
{
dfn[x]=low[x]=++ind;bl[x]=ID;w[x]=s[x]^48;
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(!dfn[v])
{
tarjan(v);low[x]=min(low[x],low[v]);w[x]+=w[v];
if(low[v]>=dfn[x]) d[x]|=(w[v]&1),f[x]++,g[x]+=w[v];
}
else low[x]=min(low[x],dfn[v]);
}
if(x==ID) --f[x];
}
void clear()
{
tot=ind=ans=c=0;
for(int i=1;i<=n;++i) head[i]=dfn[i]=low[i]=bl[i]=du[i]=d[i]=f[i]=g[i]=w[i]=0;
}
void solve()
{
n=read();m=read();
for(int i=1;i<=m;++i) add(read(),read());
scanf("%s",s+1);ans=m-n;
for(int i=1;i<=n;++i) if(!dfn[i]) ID=i,tarjan(i),++ans,c+=(w[i]&1);
writesp(c?0:fc[ans]);
for(int i=1;i<=n;++i)
{
if(d[i]) writesp(0);
else if(c-(w[bl[i]]&1)) writesp(0);
else if((w[bl[i]]-(s[i]=='1')-g[i])&1) writesp(0);
else writesp(fc[ans-du[i]+1+f[i]]);
}
puts("");clear();
}
int main()
{
#ifdef Durant_Lee
freopen("BZOJ5303.in","r",stdin);
freopen("BZOJ5303.out","w",stdout);
#endif
fc[0]=1;for(int i=1;i<N;++i)fc[i]=fc[i-1]*2%mod;
int T=read();
while(T--) solve();
return 0;
}