loj#2524bzoj5303 [Haoi2018]反色游戏(圆方树)
Posted quzhizhou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了loj#2524bzoj5303 [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;
以上是关于loj#2524bzoj5303 [Haoi2018]反色游戏(圆方树)的主要内容,如果未能解决你的问题,请参考以下文章