【loj#2524】【bzoj5303】 [Haoi2018]反色游戏(圆方树)
题意中的游戏方案可以转化为一个异或方程组的解,将边作为变量,点作为方程,因此若方程有解,方程的解的方案数就是2的自由元个数次方。我们观察一下方程,就可以发现自由元数量=边数-点数+连通块数,或者换句话说,若对原图的每个联通块指定一棵生成树,那么确定了生成树之外的边是否进行操作,那么生成树内的边的操作方案就是一定存在并唯一确定的。
那么我们就只需要判断一下什么样的图无解。我们发现每对一条边进行操作,原图内的黑点数量奇偶性不变,那么我们只需判断图中的是否存在某个联通块有奇数个黑点,若存在即无解。
加上了删点操作后,我们可以用圆方树来维护连通块信息。因为圆方树的连通性与原图上的连通性相互对应,删除单个点之后,原图被新分成的连通块就是圆方树删除对应点的连通块,那么使用圆方树就可以快速维护删除单个点的连通块信息。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #define ll long long #define inf 0x3f3f3f3f #define mod 1000000007 #define maxn 200010 inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0'; return x*f; } inline void write(ll x) { static int buf[20],len; len=0; if(x<0)x=-x,putchar('-'); for(;x;x/=10)buf[len++]=x%10; if(!len)putchar('0'); else while(len)putchar(buf[--len]+'0'); } inline void writeln(ll x){write(x); putchar('\n');} inline void writesp(ll x){write(x); putchar(' ');} struct edge{ int to,nxt; }; struct Graph{ edge e[4*maxn]; int fir[2*maxn],deg[2*maxn]; int tot; inline void clear() { memset(fir,255,sizeof(fir)); tot=0; memset(deg,0,sizeof(deg)); } inline void add_edge(int x,int y) { e[tot].to=y; e[tot].nxt=fir[x]; fir[x]=tot++; ++deg[x]; } }G,T; int dfn[maxn],low[maxn],st[maxn],ans[maxn]; int val[2*maxn],size[2*maxn],fa[2*maxn],rt[2*maxn]; char s[maxn]; int n,m,tot,tp,cnt; inline ll power(ll a,ll b) { ll ans=1; for(;b;b>>=1,a=a*a%mod) if(b&1)ans=ans*a%mod; return ans; } void tarjan(int now,int last) { dfn[now]=low[now]=++tot; st[++tp]=now; for(int i=G.fir[now];~i;i=G.e[i].nxt) if(i!=(last^1)){ if(!dfn[G.e[i].to]){ tarjan(G.e[i].to,i); low[now]=std::min(low[now],low[G.e[i].to]); if(low[G.e[i].to]>=dfn[now]){ ++cnt; T.add_edge(now,cnt); T.add_edge(cnt,now); do{ T.add_edge(st[tp],cnt); T.add_edge(cnt,st[tp]); }while(st[tp--]!=G.e[i].to); } } else low[now]=std::min(low[now],dfn[G.e[i].to]); } } void dfs(int now,int root) { rt[now]=root; size[now]=val[now]; for(int i=T.fir[now];~i;i=T.e[i].nxt) if(T.e[i].to!=fa[now]){ fa[T.e[i].to]=now; dfs(T.e[i].to,root); size[now]+=size[T.e[i].to]; } } void work() { n=read(); m=read(); G.clear(); for(int i=1;i<=m;i++){ int x=read(),y=read(); G.add_edge(x,y); G.add_edge(y,x); } scanf("%s",s); memset(val,0,sizeof(val)); for(int i=1;i<=n;i++) val[i]=(s[i-1]=='1'); T.clear(); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); tot=tp=0; cnt=n; for(int i=1;i<=n;i++) if(!dfn[i]){ tarjan(i,-1); fa[i]=-1; dfs(i,i); } int odd=0,block=0; for(int i=1;i<=n;i++) if(fa[i]==-1)odd+=(size[i]&1),++block; ans[0]=(odd?0:power(2,m-n+block)); for(int i=1;i<=n;i++){ odd-=(size[rt[i]]&1); int flag=1; for(int j=T.fir[i];~j;j=T.e[j].nxt) if(T.e[j].to!=fa[i]&&(size[T.e[j].to]&1)){ flag=0; break; } if(odd||!flag||((size[rt[i]]-size[i])&1))ans[i]=0; else ans[i]=power(2,(m-G.deg[i])-(n-1)+(block+T.deg[i]-1)); odd+=(size[rt[i]]&1); } for(int i=0;i<=n;i++) writesp(ans[i]); putchar('\n'); } int main() { int T=read(); while(T--)work(); return 0; }