题目
给你一棵n(n<=1e5)个节点的树
Alice和Bob玩游戏,Alice先手
每次轮到某玩家的时候,
①若该玩家无边可删,则该玩家输
②否则该玩家可以选择一条边,
删掉这条边,
将原树划分成两棵树,
一棵带根(root==1)的,一棵不带根的,
并把不带根的这棵树的所有边全删掉
给定这棵树,问谁必胜
思路来源
https://blog.csdn.net/clover_hxy/article/details/53836845?utm_source=blogxgwz3
https://blog.csdn.net/Code92007/article/details/87892307
IOI2009集训队论文 贾志豪《组合游戏略述——浅谈SG游戏的若干拓展及变形》
题解
又是一道AC五分钟,证明两小时的题目
树的删边定理
①叶子结点leaf的SG值sg[leaf]=0
②某棵树的根节点的SG值sg[root]=(sg[i]+1)^(sg[j]+1)^...^(sg[k]+1),其中i、j、k是其子节点,^是异或
看到定理先把题A了再说,回来再看证明
好了5min过去了题目A了,回来补证明了
这道题让我看完了贾志豪《组合游戏略述——浅谈SG游戏的若干拓展及变形》
博弈论真是深坑……
详细证明可参考上述论文……
口胡证明:
①一个节点无边可删,sg[root]=0;两个节点一条边,sg[root]=mex{sg[leaf]}=1=sg[leaf]+1,均成立
②设小于等于K个节点均成立,数归证(K+1)个节点也成立,
以下设根为点A,分A有一棵子树和A有多棵子树讨论
第一种情况:A只有一棵子树,且这棵树的根为点B,
计以A为根节点的全树为G,不包含A点和AB边的以B为根节点的树为GG
显然G的点数为(K+1),GG的点数为K
①删去边AB则必胜,故该局面存在sg=0的后继局面
②删去B这棵子树里的一条边E,E至少带走一个叶子结点,
使得G-E的点数<=K,且G-E和GG-E都具有完整树形
由归纳假设G-E的点数<=K和小于等于K个节点均成立,
设sg[树(GG-E)中的根B]=P,则sg[树(G-E)中的根A]=sg[树(GG-E)中的根B]+1=P+1,成立
③设sg[树GG中的根B]=Q,则由sg函数定义,
该树可以通过删一条边E,转移到sg值为[0,Q-1]的局面,
即sg[树(GG-E)中的根B]的值域为[0,Q-1],
由②知,sg[树(G-E)中的根A]的值域为[1,Q]
由①知,sg[树(G-E)中的根A]也可取到值0,
故sg[树(G-E)中的根A]的值域为[0,Q],
由sg函数定义,sg[树G中的根A]=Q+1=sg[树GG中的根B]+1,成立
第二种情况:A有若干棵子树,子树的根分别为B、C、D……
即证sg[A]=sg[B]^sg[C]^...^sg[D],
显然根节点对答案是没有影响的,那我们就把A拆点
有k棵子树就把A拆成k个点,每个点挂一棵子树
这样我们发现,在原树上断边的时候,一次最多断掉一棵A的子树,
这和在拆点之后的只挂一棵子树的A上进行操作是一样的,
于是,一棵树的操作就等价于多棵树的操作,
满足sg函数性质的游戏与nim游戏是一样的
第一种情况中已证一棵树的sg[root]=sg[子节点]+1
该情况下只需将多棵树的sg值异或起来,
即拆点之后的k个A点的sg值异或起来,等于拆点之前的A点的异或值
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int maxn=1e5+10;
int T,head[maxn],cnt;
struct edge{
int to,nex;
}e[maxn<<1];
void init()
{
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int u,int v)
{
e[cnt].to=v;
e[cnt].nex=head[u];
head[u]=cnt++;
}
int dfs(int u,int fa)
{
int sg=0;
for(int i=head[u];~i;i=e[i].nex)
{
int v=e[i].to;
if(v!=fa)sg^=(dfs(v,u)+1);
}
return sg;
}
int main()
{
scanf("%d",&T);
while(T--)
{
int n;
init();
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
int ans=0;
ans=dfs(1,-1);//dfs(now,fa)
if(ans)puts("Alice");
else puts("Bob");
}
return 0;
}