Alice \text{Alice} Alice 和 Bob \text{Bob} Bob 又开始玩游戏了,他们已经把石子堆得比山还高了,现在他们要玩一种更新奇的游戏,这种游戏的规则如下:
- 给定一颗 n n n 个节点的树, Alice \text{Alice} Alice 和 Bob \text{Bob} Bob 随机选择一个节点作为起点放上棋子,由 Alice \text{Alice} Alice 先手。
- 轮到一方后可以将这颗棋子移动到树上任意一点,每次一方移动的距离必须比对方上一次移动的距离还要大,开始时默认为 0 0 0。
- 当一方不能再次移动之后判负。
现在 Alice \text{Alice} Alice 和 Bob \text{Bob} Bob 已经找到了一棵节点编号为 1 ∼ n 1∼n 1∼n 的树准备开始游戏,作为博弈高手, Alice \text{Alice} Alice 和 Bob \text{Bob} Bob 均会做出最优的选择,选择一个节点后,他们知道游戏必然有一种必胜策略,现在他们想知道游戏的胜负,他们会询问你 q q q 次,每次他们会选择一个节点询问,你只需要回答在最优策略下以这个为节点为起点的胜者是谁即可。
由于每次移动的距离递增,考虑距离最大的时候,就是树的直径。这里的直径定义为一条包含最多点的路径。
显然,若一个点直径的一端,先手放这里一定会赢。于是“删除”原树上所有直径端点。
考虑现在的树的直径,现在树的直径的端点先手也是必胜的:如果先手放这里,先手可以把棋子移到现在直径的另一个端点,后手就只能移到原树直径的端点,先手移到另一个原树直径的端点,后手就操作不了了。
这样子每次删去直径的端点,直径的长度减少 2 2 2(仔细想想)。那么如果最后减少到 1 1 1 时,放这个点先手必败;如果最后减少到 2 2 2,显然先手必胜。
结论:如果树的直径长度是奇数,那么直径中间那个点 Bob \text{Bob} Bob 必胜,否则 Alice \text{Alice} Alice 必胜;如果是偶数,所有的点都是 Alice \text{Alice} Alice 必胜。
题外话,赛时我打了 70pts 暴力,剩下的就总司令(全输出 Alice \text{Alice} Alice),拿下 95pts。不可以,总司令!
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
int n,q,maxdep,D;
int head[N],nxt[N<<1],to[N<<1],cnt,dep[N],fa[N];
void add(int u,int v)
{to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void dfs(int u,int f)
{dep[u]=dep[f]+1;fa[u]=f;if(maxdep<dep[u]){maxdep=dep[u];D=u;}for(int i=head[u];i;i=nxt[i]) if(to[i]!=f) dfs(to[i],u);
}
int main()
{freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);scanf("%d%d",&n,&q);for(int i=1,x,y;i<n;i++){scanf("%d%d",&x,&y);add(x,y),add(y,x);}dfs(1,0);maxdep=0;dfs(D,0);if(maxdep&1){for(int i=1;i*2<maxdep;i++) D=fa[D];while(q--){int x;scanf("%d",&x);puts(x^D?"Alice":"Bob");}}else while(q--) puts("Alice");
}