NOIP2023模拟3联测24-博弈树

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 1n 的树准备开始游戏,作为博弈高手, 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
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");
}

你可能感兴趣的:(深度优先,图论,算法)