出题人的题解:
有个显然的结论,只有一个联通块中有偶数个节点才有解
为什么呢?我们将黑点任意两两配对,对一对黑点考虑它们之间的任意一条路径,将路径上的边的状态(操作or不操作)取反,立即可以得到一种合法方案,
那显然在一棵树上的方案数只有1种
考虑一个联通块的非树边,显然是可以替换掉树上的一条路径的
那非树边的选或不选就对应了不同的点集,则方案数就是
2
m
−
n
+
1
2^{m-n+1}
2m−n+1的
而对于一个图,方案数就是
2
m
−
n
+
p
,
p
2^{m-n+p},p
2m−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("");
}
}