Description
某一天gty在与他的妹子玩游戏。
妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问将某个节点的子树中的石子移动到这个节点先手是否有必胜策略。gty很快计算出了策略。但gty的妹子十分机智,她决定修改某个节点的石子或加入某个新节点。gty不忍心打击妹子,所以他将这个问题交给了你。另外由于gty十分绅士,所以他将先手让给了妹子。
Input
第一行两个数字,n和L,n<=5*10^4,L<=10^9
第二行n个数字,表示每个节点初始石子数。
接下来n-1行,每行两个整数u和v,表示有一条从u到v的边。
接下来一行一个数m,表示m组操作。
接下来m行,每行第一个数字表示操作类型
若为1,后跟一个数字v,表示询问在v的子树中做游戏先手是否必胜。
若为2,后跟两个数字x,y表示将节点x的石子数修改为y。
若为3,后跟三个数字u,v,x,表示为u节点添加一个儿子v,初始石子数为x。
在任意时刻,节点数不超过5*10^4。
Output
对于每个询问,若先手必胜,输出”MeiZ”,否则输出”GTY”。
另,数据进行了强制在线处理,对于m组操作,除了类型名以外,都需要异或之前回答为”MeiZ”的个数。
分析
首先考虑如果没有添加操作该怎么做。
由于每次取数的上限是m,至少要去一次,对于一个节点,如果它有x个石子,sg值就为x%(m+1)。(这个脑补一下就好了)
然后每次把一个节点的石子移动到它的父亲,也就是移到深度比它小1的节点。容易发现这是个阶梯nim,所以对于一个询问x,只把它的儿子中深度的奇偶性和它不一样的节点的sg值异或在一起。
那么先跑出整棵树的dfn,然后是区间维护两个异或和。
现在加上添加操作。
每次给u添加一个儿子v,考虑在dfs序里找到一个区间[l,r],表示以u为根的子树,然后在区间后面加上节点v。所以可以考虑用splay。在dfs序里,这个区间[l,r]中的l就是节点u,要确定区间的r在哪里,就是在l后面找到第一个深度不大于u的深度的节点。
时间复杂度O(nlogn)
/**************************************************************
Problem: 3729
User: worldwide
Language: C++
Result: Accepted
Time:816 ms
Memory:9340 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=100005,Null=maxn-1;
int root,fa[maxn],son[maxn][2],dep[maxn],min_dep[maxn],sg[maxn],xorsum_0[maxn],xorsum_1[maxn],pos[maxn],pre[maxn];
int h[maxn],e[maxn],next[maxn],tot,cnt,a[maxn],n,m,t;
void init(int x,int f,int d)
{
pos[x]=++tot;
dep[tot]=d;
sg[tot]=a[x]%(m+1);
for (int i=h[x];i;i=next[i]) if (e[i]!=f) init(e[i],x,d+1);
}
void add(int x,int y)
{
e[++tot]=y; next[tot]=h[x]; h[x]=tot;
}
void update(int x)
{
min_dep[x]=min(dep[x],min(min_dep[son[x][0]],min_dep[son[x][1]]));
xorsum_0[x]=xorsum_0[son[x][0]]^xorsum_0[son[x][1]];
xorsum_1[x]=xorsum_1[son[x][0]]^xorsum_1[son[x][1]];
if (dep[x]&1) xorsum_1[x]^=sg[x];else xorsum_0[x]^=sg[x];
}
void Rotate(int x,int t)
{
int y=fa[x];
if (fa[y]!=Null)
{
if (son[fa[y]][0]==y) son[fa[y]][0]=x;else son[fa[y]][1]=x;
}else root=x;
fa[x]=fa[y]; fa[y]=x;
son[y][t]=son[x][t^1]; if (son[y][t]!=Null) fa[son[y][t]]=y;
son[x][t^1]=y;
update(y);
}
void splay(int x,int f)
{
while (fa[x]!=f)
{
int y=fa[x];
if (fa[y]==f)
{
if (son[y][0]==x) Rotate(x,0); else Rotate(x,1);
}else
{
int z=fa[y];
if (son[z][0]==y)
{
if (son[y][0]==x)
{
Rotate(y,0); Rotate(x,0);
}else
{
Rotate(x,1); Rotate(x,0);
}
}else
{
if (son[y][0]==x)
{
Rotate(x,0); Rotate(x,1);
}else
{
Rotate(y,1); Rotate(x,1);
}
}
}
}
update(x);
}
int get(int x,int d)
{
while (1)
{
if (min_dep[son[x][0]]<=d) x=son[x][0];
else if (dep[x]<=d) return x;
else x=son[x][1];
}
}
void work()
{
int typ,x,u,v;
scanf("%d",&typ);
if (typ==1)
{
scanf("%d",&x);
x=pos[x^cnt];
splay(x,Null);
splay(get(son[x][1],dep[x]),root);
if (dep[x]&1) u=xorsum_0[son[son[x][1]][0]];else u=xorsum_1[son[son[x][1]][0]];
if (u)
{
printf("MeiZ\n"); cnt++;
}else printf("GTY\n");
}else if (typ==2)
{
scanf("%d%d",&u,&v);
u=pos[u^cnt];
sg[u]=(v^cnt)%(m+1);
splay(u,Null);
}else
{
scanf("%d%d%d",&u,&v,&x);
u=pos[u^cnt];
splay(u,Null);
int t=get(son[u][1],dep[u]);
splay(pre[t],Null);
splay(t,root);
v^=cnt;
pos[v]=++tot;
fa[tot]=son[root][1];
son[son[root][1]][0]=tot;
dep[tot]=dep[u]+1;
sg[tot]=(x^cnt)%(m+1);
pre[tot]=pre[fa[tot]];
pre[fa[tot]]=tot;
son[tot][0]=son[tot][1]=Null;
splay(tot,root);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
tot=0;
init(1,0,1);
for (int i=n+1;i>=0;i--)
{
fa[i]=i-1;
pre[i]=i-1;
if (i<=n) son[i][1]=i+1;else son[i][1]=Null;
son[i][0]=Null;
xorsum_0[i]=xorsum_0[i+1];
xorsum_1[i]=xorsum_1[i+1];
if (dep[i]&1) xorsum_1[i]^=sg[i];else xorsum_0[i]^=sg[i];
}
fa[0]=Null; dep[Null]=min_dep[Null]=maxn;
for (tot++,scanf("%d",&t);t--;work());
return 0;
}