2019CCPC秦皇岛 K MUV LUV UNLIMITED(博弈)
MUV LUV UNLIMITED
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 143 Accepted Submission(s): 16
Problem Description
联合国太平洋方面第11军横滨基地的娱乐活动很少。207小队的成员通常会在晚饭后聚在PX玩游戏。然而无论玩什么游戏,白银武总是会输。于是白银武决定利用另一个世界中的博弈论知识来让自己转败为胜。
白银武向战友们介绍了这样一个游戏:
给出一棵n个点以1为根的有根树。两个人轮流进行操作。操作人需要选出至少1个叶子(即没有儿子的点)删掉。无法操作的人输。
不幸的是,白银武发现自己的博弈论知识并不能判断自己应该选择先手还是后手。所以请你帮他判断,在双方都进行最优决策的情况下,是先手必胜还是后手必胜。
白银武向战友们介绍了这样一个游戏:
给出一棵n个点以1为根的有根树。两个人轮流进行操作。操作人需要选出至少1个叶子(即没有儿子的点)删掉。无法操作的人输。
不幸的是,白银武发现自己的博弈论知识并不能判断自己应该选择先手还是后手。所以请你帮他判断,在双方都进行最优决策的情况下,是先手必胜还是后手必胜。
Input
第1行一个整数T,代表数据组数。
对于每组数据,
第1行一个正整数n,代表树上结点个数。
接下来一行n−1个数字,依次表示2∼n点的父亲编号。
2≤n≤106
每个测试文件中的n之和不超过106。
对于每组数据,
第1行一个正整数n,代表树上结点个数。
接下来一行n−1个数字,依次表示2∼n点的父亲编号。
2≤n≤106
每个测试文件中的n之和不超过106。
Output
若在双方都选择最优决策的情况下,先手必胜,请输出"Takeru";否则输出"Meiya"。
Sample Input
2
3
1 1
4
1 2 3
Sample Output
Takeru
Meiya
Hint
对于第一组数据,先手选择删去2号点,那么后手只能删去3号点,之后先手删去1号点取得胜利。
Source
642ccpcQHD
题解:
如果存在一个叶子节点 x,且它的父亲节点的出度大于 1,那么先手一定必胜。考虑当先手只取 x 这一个节点后:变成先手必败态,那么当前先手自然是必胜的变成先手必胜态,那么存在一个方案使得去掉 x 以及其他某个叶子节点集合 S 后,能够到达一个先手必败态。而由于删除 x 后没有产生其他叶子节点,即 S 中所有点在删除 x 前就已经是叶子节点了,所以先手可以直接删除 {x}∪S 而转移到先手必败态
接下来考虑所有叶子节点的父亲的出度都等于 1 的情况。求出每个叶子节点的链长(即到达第一个出度不为 1 的祖先需要经过多少条边)。如果所有链长均为偶数则先手必败,否则先手必胜。其中必胜的策略为将所有链长为奇数的叶子删去使得他们链长变为偶数。时间复杂度:O(∑n)。
参考代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define pii pair<int,int> #define pil pair<int,ll> #define mkp make_pair const int INF=0x3f3f3f3f; const ll inf=1e18; const int maxn=1e6+10; int siz[maxn],fa[maxn],cnt[maxn]; int T,n,m; vector<int> vec; struct Edge{ int v,nxt; } edge[maxn<<1]; int head[maxn],tot; void Init() { tot=0; fa[1]=-1; for(int i=0;i<=n;++i) head[i]=-1,cnt[i]=0; vec.clear(); } void AddEdge(int x,int y) { edge[tot].v=y; edge[tot].nxt=head[x]; head[x]=tot++; } void dfs(int u) { for(int i=head[u];~i;i=edge[i].nxt) { int v=edge[i].v; if(v==fa[u]) continue; fa[v]=u; dfs(v); } } int main() { scanf("%d",&T) ; while(T--) { scanf("%d",&n); Init(); for(int i=2;i<=n;++i) { int x; scanf("%d",&x); cnt[x]++;cnt[i]++; AddEdge(i,x);AddEdge(x,i); } dfs(1); cnt[1]++; for(int i=1;i<=n;++i) if(cnt[i]==1) vec.push_back(i); int flag=0; for(int i=0,len=vec.size();i<len;++i) { int v=vec[i]; if(cnt[fa[v]]>2) {flag=1;break;} int res=1; while(cnt[fa[v]]==2) { v=fa[v]; res++; } if(res&1) flag=1; } if(flag==1) puts("Takeru"); else puts("Meiya"); } return 0; }