题面
https://www.luogu.org/problem/P4494
题解
$2018$蛤省省选神仙题。
前置知识:线性基。$orz aysn$
关键的一个结论:在一个任意的联通块内,如果它的黑点个数为偶数个,则“联通块内的目标”是可以达到的。设点数为$n$,边数为$m$,则方案数为$2^{m-n+1}$,这个结论的证明需要线性基,将任意一个生成树内的边全部插入线性基,就构成了一个可以表达任意数的线性基,可以证明,加入其他的边是冗杂的,无论线性基外的边怎么选,线性基里面的边总可以使他变回全$0$的状态。如果任意一个线性基中的边不按它“应该”在的状态,都是不行的。当黑点的个数为奇数个时,是无法通过异或得到的。
所以直接$tarjan$一发就好了,三个判断分别是判断去掉$x$后下面的点双是不是奇数个黑点、其他联通块是不是奇数个黑点、$x$上面的点双是不是奇数个黑点。
突然想起来刘汝佳紫书上的一道题,和异或有关的,是研学游在大巴上想的,也许和这个有关吧。
#include<vector> #include<cstdio> #include<cstring> #include<iostream> #define N 100500 #define mod 1000000007 #define ri register int using namespace std; inline int read() { int ret=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9') ret*=10,ret+=(ch-'0'),ch=getchar(); return ret; } vector<int> to[N]; char ch[N]; int n,m,a[N],mi[N<<1]; int dfn[N],low[N],vis[N],clc,rt; int sum[N],c[N],d[N],f[N],dg[N]; void init() { clc=0; for (ri i=1;i<=n;i++) dfn[i]=low[i]=d[i]=c[i]=f[i]=0; for (ri i=1;i<=n;i++) to[i].clear(); } void tarjan(int x) { dfn[x]=low[x]=++clc; vis[x]=rt; sum[x]=a[x]; for (ri i=0;i<to[x].size();i++) { int y=to[x][i]; if (!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); sum[x]+=sum[y]; if (low[y]>=dfn[x]) { d[x]|=(sum[y]&1),c[x]++,f[x]+=sum[y]; } } else low[x]=min(low[x],dfn[y]); } if (x==rt) c[x]--; } int main() { mi[0]=1; for (ri i=1;i<(N<<1);i++) mi[i]=(mi[i-1]<<1)%mod; int T=read(); while (T--) { n=read();m=read();init(); int val=0,tot=0; for (ri i=1;i<=m;i++) { int u=read(),v=read(); to[u].push_back(v); to[v].push_back(u); } for (ri i=1;i<=n;i++) dg[i]=to[i].size(); scanf("%s",ch+1); for (ri i=1;i<=n;i++) a[i]=ch[i]-'0'; for (ri i=1;i<=n;i++) if (!dfn[i]) { rt=i; tarjan(i); ++tot; val+=sum[i]&1; } printf("%d ",val?0:mi[m-n+tot]); for (ri 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 ",mi[m-n+tot-dg[i]+1+c[i]]); } puts(""); } return 0; }