【BZOJ#3729】Gty的游戏
题目
题目链接:https://darkbzoj.tk/problem/3729
某一天 gty 在与他的妹子玩游戏。
妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于 \(L\) 的石子移动到父节点,询问将某个节点的子树中的石子移动到这个节点先手是否有必胜策略。
gty 很快计算出了策略。
但 gty 的妹子十分机智,她决定修改某个节点的石子或加入某个新节点。
gty 不忍心打击妹子,所以他将这个问题交给了你。
另外由于 gty 十分绅士,所以他将先手让给了妹子。
\(n,m\leq 5\times 10^4\)。
思路
首先每一个节点上的石子数量可以先对 \(L+1\) 取模。不会证记结论(
然后一个点 \(x\) 为根的子树的博弈其实就是一个阶梯博弈,直接把奇数层和偶数层的石子数量异或和预处理一下即可。
然后考虑带修改的。可以用一棵 Splay 维护 深度为奇数/偶数节点的异或和。
因为 dfs 序会改变,询问的时候不能直接根据子树大小找区间,所以我们在 Splay 上每个节点维护子树最小深度,那么询问 \(x\) 子树内的博弈结果就可以先把 \(x\) 旋到根,然后在它右子树内找到第一个子树最小深度不超过 \(\text{dep}_x\) 的点旋转到 \(x\) 的右儿子处,然后 \(x\) 右儿子的左儿子的子树就是原树上 \(x\) 的子树内的点。
修改点权就直接改然后 Splay 一下,加入节点就依然找到子树区间直接插进去即可。
时间复杂度 \(O(m\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,rt,L,tot,cnt,head[N],dep[N];
struct edge
{
int next,to;
}e[N*2];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
struct Splay
{
int val[N],res[N][2],mind[N],ch[N][2],fa[N];
void build() { rt=N-1; ch[rt][1]=N-2; fa[N-2]=rt; mind[0]=1e9; }
void New(int x) { res[x][dep[x]&1]=val[x]; mind[x]=dep[x]; }
int pos(int x) { return ch[fa[x]][1]==x; }
void pushup(int x)
{
if (!x) return;
int lc=ch[x][0],rc=ch[x][1];
res[x][0]=res[lc][0]^res[rc][0];
res[x][1]=res[lc][1]^res[rc][1];
res[x][dep[x]&1]^=val[x];
mind[x]=min(dep[x],min(mind[lc],mind[rc]));
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=pos(x),c=ch[x][k^1];
ch[z][pos(y)]=x; ch[x][k^1]=y; ch[y][k]=c;
fa[c]=y; fa[y]=x; fa[x]=z;
pushup(y); pushup(x);
}
void splay(int x,int f=0)
{
for (;fa[x]!=f;rotate(x))
{
int y=fa[x];
if (fa[y]!=f) rotate(pos(x)==pos(y) ? y : x);
}
if (!f) rt=x;
}
int find(int x,int d)
{
if (ch[x][0] && mind[ch[x][0]]<=d) return find(ch[x][0],d);
if (dep[x]<=d) return x;
return find(ch[x][1],d);
}
void ins(int x,int y)
{
splay(y);
splay(find(ch[y][1],dep[y]),y);
y=ch[y][1];
ch[x][0]=ch[y][0]; fa[ch[y][0]]=x;
ch[y][0]=x; fa[x]=y;
pushup(x); pushup(y);
splay(x);
}
}splay;
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;
splay.New(x); splay.ins(x,fa);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa) dfs(v,x);
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&L);
for (int i=1;i<=n;i++)
{
scanf("%d",&splay.val[i]);
splay.val[i]%=(L+1);
}
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
splay.build();
dfs(1,rt);
scanf("%d",&m);
while (m--)
{
int opt,x,y,z;
scanf("%d",&opt);
if (opt==1)
{
scanf("%d",&x);
x^=cnt;
splay.splay(x);
splay.splay(splay.find(splay.ch[x][1],dep[x]),x);
int y=splay.ch[splay.ch[x][1]][0];
int ans=splay.res[y][(dep[x]&1)^1];
if (ans) printf("MeiZ\n"),cnt++;
else printf("GTY\n");
}
if (opt==2)
{
scanf("%d%d",&x,&y);
x^=cnt; y=(y^cnt)%(L+1);
splay.val[x]=y; splay.pushup(x);
splay.splay(x);
}
if (opt==3)
{
scanf("%d%d%d",&x,&y,&z);
x^=cnt; y^=cnt; z=(z^cnt)%(L+1);
dep[y]=dep[x]+1;
splay.val[y]=z; splay.New(y);
splay.ins(y,x);
}
}
return 0;
}