洛谷 P2972:Rocks and Trees G ← 树上Nim博弈

【题目来源】
https://www.luogu.com.cn/problem/P2972

【题目描述】
两个人在一棵有根树上玩 Nim 阶梯游戏。
给出一棵有 N 个结点的有根树(节点 1 为根),每个结点有两个属性 Pi 和 Ri,Pi 表示结点 i 的父亲结点,Ri 表示结点 i 的石头数(结点 1 没有石头)。
游戏在两个玩家之间轮流进行,Ted 先手。在每一轮,这轮的玩家可以选择一个非根结点,并且把最多 L 个石头从这个结点向树根靠近一个单位(也就是说,把这些石头移动到它的父结点处)。并且这个玩家至少需要移动一个石子。
当某个玩家没有办法移动石子的时候(也就是所有的石子都移动到结点 1),游戏结束,这个玩家失败。
Ted 将会对布局进行 T 次修改。请帮助他确定,在每步修改之后,以这个布局开局,在双方都用最优策略的前提下他是否能赢得这个游戏。
Ted 的每次修改由两个数字 x 和 y 描述,表示 Ted 将会把结点 x 的石头数修改为 y(注意这是一个“设定”操作,既不是“减少”也不是“增加”)。并且询问修改后谁会获胜。这些修改会累积保持,也就是若往后的操作结点 x 的石头数没有修改,则结点 x 的石头数会保持在 y个。

【输入格式】
第 1 行:三个空格分隔的整数 N、T 和 L。
第 2~N 行:每行包含两个空格分隔的整数 Pi (1≤Pi<i)和 Ri(1≤Ri≤1000)。
接下来 T 行:每行两个整数 Aj(1<Aj≤N) 及 Bj(1≤Bj≤1000),表示 Ted 的每次操作。

【输出格式】
输出 T 行。如果在第 i 次修改后,Ted 可以获胜,那么第 i 行输出 Yes,否则输出 No。

【输入样例】
3 2 10 
1 5 
1 3 
2 3 
3 1

【输出样例】
No 
Yes

【数据范围】
2≤N≤10^4,1≤T≤10^4,1≤L≤10^3

【算法分析】
● “洛谷 P2972:Rocks and Trees G”整体是树上台阶 Nim,即将台阶 Nim 的线性台阶扩展到树形结构:深度奇偶替代台阶奇偶,每次操作将石子从非根节点移至父节点,偶数深度节点可作为“安全缓冲区”被对手模仿抵消,因此只有奇数深度节点参与胜负判定。每个奇数深度节点上的石子受取子上限 L 限制,每次只能取 1 到 L 颗,这使其单堆退化为巴什博奕,SG 值为 cnt % (L+1)。最终将各奇数深度节点的 SG 值异或,若异或和不为零则先手必胜,否则先手必败。

● 巴什博弈(Bash Game)是一个涉及两名玩家的双人博弈,属于公平组合游戏(ICG, Impartial Combinatorial Game)的典型例子。博弈中有一堆总数为 n 的物品,两名玩家轮流从中拿取物品,每次至少拿 1 件,至多拿 m 件,不能不拿,最终将物品拿完者获胜。分析如下:
(1)n≤m 时,由于一次最少拿一个,最多拿 m 个,甲可以一次拿完,先手赢。
(2)n=m+1 时,无论甲拿走多少个 (1~m 个),剩下的都多于 1 个且少于或等于 m 个,乙都能一次拿走剩余的石子,后手取胜。
上面两种情况可以扩展为以下两种情况。
(1)如果 n%(m+1)==0,即 n 是 m+1 的整数倍,那么不管甲拿多少,如 k 个,乙都拿 m+1-k 个,使剩下的永远是 m+1 的整数倍,直到最后的 m+1 个,所以后拿的乙一定赢。
(2)如果 n%(m+1)!=0,即 n 不是 m+1 的整数倍,还有余数 r,那么甲拿走 r 个,剩下的是 m+1 的倍数,这样就转移到了情况(1),相当于甲乙互换,结果是甲赢。
在这个拿石子的游戏中,对于后拿的乙来说是很不利的,只有在 n%(m+1)=0 的情况下乙才能赢,其他所有情况都是甲赢。

巴什博奕(Bash Game)是组合博弈中的一类基础游戏,特指单堆石子、每次可取 1 到 L 颗、取走最后一颗者胜的独立玩法。

【算法代码】

#include <bits/stdc++.h>
using namespace std;

const int N=1e4+5;
int fa[N],cnt[N],dep[N];
vector<int> g[N];
int n,T,L;
int xor_sum;

void dfs(int u) {
    for(int v:g[u]) {
        dep[v]=dep[u]+1;
        dfs(v);
        if(dep[v]&1) xor_sum^=(cnt[v]%(L+1));
    }
}

int main() {
    cin>>n>>T>>L;
    for(int i=2; i<=n; i++) {
        cin>>fa[i]>>cnt[i];
        g[fa[i]].push_back(i);
    }

    dfs(1);

    while(T--) {
        int pos,val;
        cin>>pos>>val;
        if(dep[pos]&1) {
            xor_sum^=(cnt[pos]%(L+1));
            xor_sum^=(val%(L+1));
        }
        cnt[pos]=val;
        cout<<(xor_sum?"Yes\n":"No\n");
    }
    return 0;
}

/*
in:
3 2 10
1 5
1 3
2 3
3 1

out:
No
Yes
*/



【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/158802621
https://blog.csdn.net/hnjzsyjyj/article/details/158893015

 

posted @ 2026-08-05 19:25  Triwa  阅读(2)  评论(0)    收藏  举报