1. 程式人生 > >【題解】Luogu P2972 [USACO10HOL]巖石和樹木Rocks and Trees

【題解】Luogu P2972 [USACO10HOL]巖石和樹木Rocks and Trees

class detail max reg name 需要 保存 ++ 深度

關於Nim遊戲,sg函數及其一些變形可以戳這位大佬的blog:

https://blog.csdn.net/clover_hxy/article/details/53818624


Solution

這是一道階梯Nim遊戲的題,與普通的階梯Nim遊戲不同之處在於它是在一棵樹上移動,實際上就是多個階梯Nim遊戲的復合

我們知道,階梯Nim遊戲可以視作對奇數階梯上上的石子做Nim,證明在上方的blog中有提及,在此不再贅述

在此題中,設\(1\)號節點的深度為\(0\),那麽深度為奇數的節點即為“奇數階梯”

考慮每次對節點上石頭數量的修改,我們可以保存上一次修改後求出的\(sg\)值的異或和\(x\)

如果修改的節點的深度為偶數,對答案並沒有貢獻,直接判斷上一次的\(x\)

即可

如果修改的節點的深度為奇數,由於異或滿足自反性(即\(x\) xor \(x\) \(=0\)),所以在修改之前,我們只需要對\(x\)異或一遍修改前的數,再異或一遍修改後的數即可,而沒有必要重新求一遍異或和

Code

#include <cstdio>
#define maxn 10005
#define maxL 1005
using namespace std;

int n,T,L;
int r[maxn],prt[maxn],dep[maxn];
int sg[maxL];

int x;
inline bool Solve(int pos,int chg)
{
    if(!(dep[pos]&1))
        return x;
    x^=sg[r[pos]];
    x^=sg[r[pos]=chg];
    return x;
}

int main()
{
    scanf("%d%d%d",&n,&T,&L);
    register int i;
    int a,b;
    for(i=1;i<=1000;++i)
        sg[i]=i%(L+1);
    for(i=2;i<=n;++i)
    {
        scanf("%d%d",&prt[i],&r[i]);
        dep[i]=dep[prt[i]]+1;
        if(dep[i]&1)
            x^=sg[r[i]];
    }
    for(i=1;i<=T;++i)
    {
        scanf("%d%d",&a,&b);
        if(Solve(a,b))
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

【題解】Luogu P2972 [USACO10HOL]巖石和樹木Rocks and Trees