石子游戏 解题报告
题目描述
给定一个 \(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;
}

浙公网安备 33010602011771号