【博弈】2019CCPC秦皇島賽區 - K - MUV LUV UNLIMITED

題目鏈接http://acm.hdu.edu.cn/showproblem.php?pid=6741


題意

一棵樹上,兩個人進行博弈,每次操作至少刪去一個葉子節點,不能刪的人輸。


題解

首先瞎編一個定義:一個葉子節點一直往上到分叉點爲止(不包括分岔點)的這一條鏈稱爲一個枝條。

如果某棵樹上存在一個長度爲1的枝條,那麼先手必勝。這個我們可以分兩種情況進行證明:

  • 如果刪去這根長度爲1的枝條,剩下的樹先手必敗,那麼我們只需要刪去這根長度爲1的枝條,把必敗的狀態扔給對面就行了
  • 如果刪去這跟長度爲1的枝條,剩下的樹先手必勝,那麼我們就刪去剩下的樹先手應該刪的點,順帶刪掉我們這個長度爲1的枝條。

所以如果存在長度爲1的枝條,先手必勝。那如果有人碰到枝條長度全是2的情況,就必敗,因爲它無論怎麼刪,必然製造出長度爲1的枝條。

這樣子的話,我們就可以把這棵樹所有的枝條都拿出來,把他們的長度減去2,放進一個數組裏。如果把數組裏的數看成石子,題目轉化成有若干個石子堆,每次至少拿走一顆石子,每個石子堆至多拿走一顆石子,不能拿的人輸。

這就是個比較簡單的博弈題,如果石子堆個數都是偶數,那先手必敗,因爲先手無論怎麼取,後手都可以做一樣的操作。如果存在奇數個石子堆,那先手必勝,他只需要把所有石子堆變成偶數即可。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<ll,int>piir;
const int N=2e6+7;
int f[N],d[N];
int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=0;i<=n;i++) f[i]=0,d[i]=0;
        for(int i=2,x;i<=n;i++){
            scanf("%d",&x);
            d[x]++;
            f[i]=x;
        }
        bool flag=false;
        for(int i=1;i<=n;i++){
            int cnt=0;
            if(d[i]==0){
                int u=i;
                while(u!=0&&d[u]<=1){
                    cnt++;
                    u=f[u];
                }
                if(cnt&1){flag=true;break;}
            }
        }
        if(flag) printf("Takeru\n");
        else printf("Meiya\n");
    }
}

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