【loj#2524】【bzoj5303】 [Haoi2018]反色游戏(圆方树)
生活随笔
收集整理的這篇文章主要介紹了
【loj#2524】【bzoj5303】 [Haoi2018]反色游戏(圆方树)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目傳送門:loj bzoj
題意中的游戲方案可以轉化為一個異或方程組的解,將邊作為變量,點作為方程,因此若方程有解,方程的解的方案數就是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; } 反色游戲?
轉載于:https://www.cnblogs.com/quzhizhou/p/11483149.html
總結
以上是生活随笔為你收集整理的【loj#2524】【bzoj5303】 [Haoi2018]反色游戏(圆方树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 雷神笔记本怎么原版安装系统 雷神笔记本如
- 下一篇: win10开机密码u盘怎么取消 取消wi