題目鏈接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");
}
}