POJ 3710:Christmas Game
阿新 • • 發佈:2018-07-15
min 可能 奇偶性 out names 我們 會有 void iostream
假設是偶環,實際上就沒有貢獻了,直接把它去掉就可以了
假設是奇環,那麽會有 \(1\) 的貢獻,我們直接在這個葉子節點後面接上一個點就可以達到這樣的效果了
Description
在樹上掛幾個環,每一個環只與樹有一個公共點,環與環之間無公共邊,每次刪掉一條邊,把不與根節點相連的部分刪除,不能操作者輸,問是否先手必勝。
題面
Solution
由於環是在葉子上的,所以這個環可以單獨考慮
假設這個環是奇環,那麽砍掉任意一條邊之後,就會變成從葉子節點延伸出來的兩條鏈,且這兩條鏈奇偶性相同,所以後繼狀態的 \(SG\) 異或起來不可能為奇數,並且可以為 \(0\),所以 \(SG=mex(SG[u])=1\)
假設這個環是偶環,那麽這兩條鏈的奇偶性不同,所以異或起來的所有情況中一定沒有 \(0\),所以 \(SG=mex=0\)
那麽我們就可以把環縮成一個點了
假設是奇環,那麽會有 \(1\) 的貢獻,我們直接在這個葉子節點後面接上一個點就可以達到這樣的效果了
那麽現在問題就轉化為了樹上刪邊遊戲,利用結論:節點 \(x\) 的 \(SG\) 等於所有兒子 \(u\) 的 \((SG[u]+1)\) 的異或和
這樣就推出了根節點的 \(SG\)
因為有很多棵樹,再把所有根節點的 \(SG\) 異或起來就行了
還有就是這個題仿佛是可以有點雙的?
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstdio> using namespace std; const int N=2010; int n,m,T,sz[N],head[N],to[N*2],nxt[N*2],num=1;bool vis[N]; int dfn[N],low[N],DFN=0,st[N],top=0,sum,q[N],r=0; inline void Clear(){ num=1;DFN=sum=0; for(register int i=0;i<N;i++)sz[i]=head[i]=dfn[i]=low[i]=vis[i]=0; } inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;} inline void tarjan(int x,int last){ dfn[x]=low[x]=++DFN;st[++top]=x; for(int i=head[x];i;i=nxt[i]){ if(i==last || to[i]>n)continue; int u=to[i]; if(!dfn[u]){ tarjan(u,i^1),low[x]=min(low[x],low[u]); if(low[u]>=dfn[x]){ int sz=2;r=0; while(top && st[top]!=u)q[++r]=st[top--],sz++; if(top)q[++r]=st[top--]; if(sz>2)while(r)vis[q[r--]]=1; if(sz>2 && (sz&1))link(x,++sum); } } else low[x]=min(low[x],dfn[u]); } } inline int dfs(int x,int last){ int ret=0; for(int i=head[x];i;i=nxt[i]){ int u=to[i]; if(u==last || vis[u])continue; ret^=(dfs(u,x)+1); } return ret; } inline int work(){ Clear(); int x,y; scanf("%d%d",&n,&m);sum=n; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); link(x,y);link(y,x); } tarjan(1,0); return dfs(1,1); } int main(){ freopen("pp.in","r",stdin); freopen("pp.out","w",stdout); while(~scanf("%d",&T)){ int ans=0; while(T--)ans^=work(); if(ans)puts("Sally"); else puts("Harry"); } return 0; }
POJ 3710:Christmas Game