石子游戏 解题报告

题目描述

给定一个 \(n\) 个点的树,每一个点有点权 \(a_i\) 。再给定一个数 \(m\)

一次游戏的定义如下:

两个玩家轮流行动,初始时 \(i\) 号节点上有 \(a_i\) 个石子。每一次行动可以选择一个根节点以外的节点,将该点上的石子往其父亲移动 \(1\)\(m\) 个。最后不能移动者输。

特别地,每次游戏独立。

\(t\) 次操作,每次操作形如:

  • 询问在以 \(u\) 点为根的子树内进行游戏,问先手是否必胜;

  • 修改一个点的点权;

  • 添加一个新点

强制在线。

数据范围:\(n,t \le 5 \times 10^4\)

分析

先考虑不带修怎么做。

首先这个游戏形式就很像阶梯 Nim(因为如果移动到偶数深度的节点,那后手一定可以再移动到奇数深度的节点),然后发现对于每一个节点,它 SG 函数的值等于 \(a_i \bmod (m+1)\)(证明可以自行搜素“巴什博弈“)。因此对于每个节点,我们只需要维护它子树内和它距离为奇数的节点的 SG 函数值的异或和。

然后再考虑修改。

看到 $ 5 \times 10^4$,容易想到带根号的算法,于是考虑时间轴分块(操作分块?)。

假设块长为 \(B\)

\(B\) 次操作我们就暴力搜索一次来维护之前操作的贡献,时间复杂度 \(O(\frac{nq}{B})\)

每一次询问中:因为已经预处理了整块的贡献,使用只需要处理散块的贡献即可。暴力枚举每一次在散块中的操作,如果当前操作的点在询问点的子树内且和询问的距离为奇数,那就维护贡献,时间复杂度 \(O(qB)\)

总时间复杂度为 \(O(\frac{nq}{B}+qB)=O(q\sqrt n)\),当且仅当 \(B=\sqrt n\) 时,等式成立。

然后稍微注意一点细节即可。

代码

const int N=2e6+1000,SZ=224;
int n,m,a[N],b[N],q,mod;
bool vis[N];
struct Edge{int from,to;}e[N<<1];
int num,h[N];
void add(int f,int t){e[++num].from=h[f],e[num].to=t,h[f]=num;}
int sz[N],dep[N],dfn[N],dfu,f[N];
int val[N][2];
void dfs(int u,int fa){
    dfn[u]=++dfu,sz[u]=1,vis[u]=1;
    dep[u]=dep[fa]+1;
    val[u][0]=a[u];
    val[u][1]=0;
    for(int i=h[u];i;i=e[i].from){
        int v=e[i].to;
        if(v==fa) continue;
        dfs(v,u);
        sz[u]+=sz[v];
        val[u][0]=val[u][0]^val[v][1];
        val[u][1]=val[u][1]^val[v][0];
    }
}
int cnt=0,p[N];
struct Act{int u,val;}pt[N];
void rebuild(){
    For(i,1,cnt) b[pt[i].u]=a[pt[i].u]=pt[i].val;
    dfu=cnt=0;
    dfs(1,0);
}
int lstans,res;
void solve(int p){
    res=val[p][1];
    For(i,1,cnt){
        int u=pt[i].u,x=pt[i].val;
        if(dep[u]%2==dep[p]%2) continue;
        if(!vis[p]){
            if(vis[u] || dep[u]<=dep[p]) continue;
            int t=u;
            Down(i,dep[u]-dep[p],1) t=f[t];
            if(t!=p) continue;
        }
        else  if(dfn[u]<dfn[p] || dfn[p]+sz[p]<=dfn[u]) 
            continue;
        res^=b[u]^x;
        b[u]=x;
    }
    For(i,1,cnt)
        b[pt[i].u]=a[pt[i].u];
    printf(res ? "Yes\n":"No\n");
    lstans+=(res!=0);
}
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    n=read(),m=read(),mod=m+1;
    For(i,1,n) b[i]=a[i]=read()%mod;
    int u,v;
    For(i,2,n) u=read(),v=read(),add(u,v),add(v,u);
    q=read();
    rebuild();
    int opt,x;
    For(i,1,q){
        if(i%SZ==0) rebuild();
        opt=read(),u=read()^lstans;
        if(opt==1)
            solve(u);
        else if(opt==2)
            x=(read()^lstans)%mod,pt[++cnt]=(Act){u,x};
        else if(opt==3){
            v=read()^lstans,x=(read()^lstans)%mod;
            dep[v]=dep[u]+1,f[v]=u;
            add(u,v),add(v,u),dfn[v]=dfn[u];
            pt[++cnt]=(Act){v,x};
        }
    }
    return 0;
}
posted @ 2025-08-19 11:47  XiaoZi_qwq  阅读(11)  评论(0)    收藏  举报