牛客練習賽63 F 牛牛的樹行棋 (SG函數+樹差分)

鏈接:https://ac.nowcoder.com/acm/contest/5531/F
來源:牛客網
 

牛牛的樹行棋

時間限制:C/C++ 2秒,其他語言4秒
空間限制:C/C++ 524288K,其他語言1048576K
64bit IO Format: %lld

題目描述

牛牛和牛妹是一對好朋友,這天他們倆決定在樹上玩一個遊戲。

遊戲的名字是“樹行棋”,規則如下:

 

給定一個含有n個節點分別從1到n編號且以節點1爲根的樹,一開始每個節點各有1個棋子。

牛牛和牛妹輪流進行操作,且牛牛先手移動。

 

每次操作可以選擇將任意一個棋子移動到其子樹中的任意一個節點,但是每次移動必須保證棋子的位置發生變化(不能拿起再放回原處)。

直到無法進行操作,輪到不能操作的一方即爲敗者。


現在假設雙方都採用最優策略,請問牛牛能否贏。
如果牛牛(先手)能夠贏,請問牛牛第一步有多少種不同的必勝策略。


我們認爲兩個策略是不同的,當且僅當這兩個策略一開始選擇的棋子不同,或者移動的路徑不同。
 

輸入描述:

第一行輸入一個n,(1≤n≤5×105)n,(1 \leq n \leq 5 \times 10^5)n,(1≤n≤5×105),表示樹含有n個點。
接下來n-1行,每行輸入一個x,y,(1≤x,y≤n1\leq x,y \leq n1≤x,y≤n)表示x與y相連。

輸出描述:

對於每組數據,如果牛牛(先手)能贏則輸出‘YES’,否則輸出‘NO’。
如果能贏,請在第二行跟着輸出一個正整數ans,表示第一步的必勝策略數目。

示例1

輸入

複製3 1 2 1 3

3
1 2
1 3

輸出

複製YES 2

YES
2

說明


 

因爲2,3兩個節點爲葉節點,題目要求每次操作棋子的位置必須產生變化,所以2,3兩節點的棋子是無法移動的。

只能操作節點1上的棋子,可以選擇將其移動到2或者移動到3,所以共2種必勝決策。

示例2

輸入

複製4 1 2 2 3 3 4

4
1 2
2 3
3 4

輸出

複製NO

NO

說明


 

一上來牛牛有6種移動方案:

1、將1號節點上的棋子移動到2

此時2號節點有兩個棋子,3號節點4號節點各有1個棋子,且4號節點的棋子無法移動。

後手只要選擇3號節點的棋子將其移動到4號節點,接下來後手完全模仿先手的操作就可以勝利。

...(剩下5種方案由於類似的原因都是後手取得勝利)

備註:

注意每次操作是將一個棋子移動至子樹中的任意一個節點,並不是移動到直接兒子。

思路:

一個狀態的 SG 函數值,就是其所有後繼狀態(進行一次操作後所到達的狀態)

\dpi{120} \bg_white SG_{x}=mex\left \{SG_{y}|y\in STA_{x}}{ \right \},其中STAx爲x的後繼狀態。

有以下兩個結論:

① 若當前局面的 SG 函數值爲 0,先手必敗,否則先手必勝。

② 若一個遊戲是多個獨立遊戲組成的,那麼當前局面的 SG 函數值是多個獨立遊戲 SG 函數值的異或 (^)。

對於這道題,顯然每個棋子都是獨立的。也就是每個棋子都構成了一個獨立遊戲。因此當前局面的SG函數值就是每個棋子的SG函數值的異或和。

可以推出,葉子結點的SG函數值爲0,葉子結點的父節點的SG函數值爲1,以此類推,結點u的SG函數值就是以u爲根的子樹中,與u離的最遠的那個葉子結點的距離。因此,可以O(n)的進行一遍dfs以求出所有節點的SG函數值(記它們的異或和爲val)。

若val=0,則先手必敗,否則先手必勝。下面計算勝利的方案數。

假設結點u的SG函數值爲x[u],那麼只要把它移動到其子樹中SG函數值爲x[u]^val的節點,那麼新的局面的SG函數值爲val^x[u]^x[u]^val=0,這樣就讓對手進入了必敗態。差分一下,可以O(n)求出答案。

注意:記錄SG函數值爲x[u]的節點個數時,要注意數組的大小。因爲異或可以使val的值超過n!!!

代碼:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int maxn=5e5+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
int n,m,k,ct;
int a[maxn],x[maxn],cnt[maxn<<2];
int he[maxn],ans,val;
struct node{
    int v,nxt;
}e[maxn<<1];
template <typename T>
inline void read(T &X)
{
    X=0;int w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    if(w) X=-X;
}
void add(int u,int v){
    e[ct].v=v;
    e[ct].nxt=he[u];
    he[u]=ct++;
}
void dfs(int u,int fa){
    x[u]=0;
    for(int i=he[u];i!=-1;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa) continue;
        dfs(v,u);
        x[u]=max(x[u],x[v]+1);
    }
    val^=x[u];
}
void dfs1(int u,int fa){
    ans-=cnt[x[u]^val];
    cnt[x[u]]++;
    for(int i=he[u];i!=-1;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa) continue;
        dfs1(v,u);
    }
    ans+=cnt[x[u]^val];
}
int main()
{
    int T,cas=1;
    //read(T);
    //while(T--)
    {
        read(n);
        rep(i,1,n) he[i]=-1;
        rep(i,1,n-1){
            int x,y;
            read(x);read(y);
            add(x,y);
            add(y,x);
        }
        dfs(1,-1);
        dfs1(1,-1);
        if(val) printf("YES\n%d\n",ans);
        else puts("NO");
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章