1. 程式人生 > >POJ 3710:Christmas Game

POJ 3710:Christmas Game

min 可能 奇偶性 out names 我們 會有 void iostream

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