bzoj3729: Gty的游戏【博弈论+splay】

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”的个数。

Sample Input

2 1000

0 0

1 2

1

1 1

Sample Output

GTY

解题思路:

首先根据经典Nim定理: sg[x]=mexmi=1sg[xi] s g [ x ] = m e x i = 1 m s g [ x − i ]
所以 sg[x]=xmod(m+1) s g [ x ] = x mod ( m + 1 )
而此题只移动一步是阶梯Nim模型,只用关心相对于根深度为奇数的点的异或和即可,因为如果你移动偶数层,别人就可以立马把它移回偶数层,只有对方有利。
所以用splay动态维护dfs序和区间内奇偶层的异或和。
而一个节点的子树区间只用去找它右边第一个深度小等于它的点即可,所以splay还要维护区间最小深度,方便在树上二分查找。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=200005;
int n,m,cnt,mod;
vector<int>g[N];
int root,son[N][2],fa[N],val[N],sum[N][2],dep[N],min_dep[N];
inline int which(int x){return son[fa[x]][1]==x;}
inline void update(int x)
{
    int l=son[x][0],r=son[x][1];
    sum[x][0]=sum[l][0]^sum[r][0];
    sum[x][1]=sum[l][1]^sum[r][1];
    sum[x][dep[x]&1]^=val[x];
    min_dep[x]=min(min(min_dep[l],min_dep[r]),dep[x]);
}
void rotate(int x)
{
    int y=fa[x],z=fa[y],t=which(x);
    if(y!=root)son[z][which(y)]=x;
    else root=x;
    fa[x]=z,fa[y]=x;
    son[y][t]=son[x][t^1],son[x][t^1]=y;
    if(son[y][t])fa[son[y][t]]=y;
    update(y),update(x);
}
void splay(int x,int tag)
{
    while(fa[x]!=tag)
    {
        if(fa[fa[x]]!=tag)
            which(x)==which(fa[x])?rotate(fa[x]):rotate(x);
        rotate(x);
    }
}
void dfs(int u,int f)
{
    son[root][1]=u,fa[u]=root,splay(u,0);
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==f)continue;
        dep[v]=dep[u]+1;
        dfs(v,u);
    }
}
int find_pre(int x)
{
    x=son[x][0];
    while(son[x][1])x=son[x][1];
    return x;
}
int find_ed(int x)
{
    splay(x,0);
    int y=son[x][1];
    while(1)
    {
        if(min_dep[son[y][0]]<=dep[x])y=son[y][0];
        else if(dep[y]<=dep[x])break;
        else y=son[y][1];
    }
    return y;
}
void query()
{
    int x=getint()^cnt,y=find_ed(x);
    splay(find_pre(x),0),splay(y,root);
    y=son[son[root][1]][0];
    if(sum[y][(dep[x]&1)^1])puts("MeiZ"),cnt++;
    else puts("GTY");
}
void modify()
{
    int x=getint()^cnt;
    splay(x,0),val[x]=(getint()^cnt)%mod;
    update(x);
}
void extend()
{
    int x=getint()^cnt,y=getint()^cnt;
    val[y]=(getint()^cnt)%mod;dep[y]=dep[x]+1;
    splay(x,0);
    son[y][1]=son[x][1],fa[son[x][1]]=y,son[x][1]=y,fa[y]=x;
    update(y),update(x);
}
int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    int op,x,y;dep[0]=min_dep[0]=1e9;
    root=200000;
    n=getint(),mod=getint()+1;
    for(int i=1;i<=n;i++)val[i]=getint()%mod;
    for(int i=1;i<n;i++)
    {
        x=getint(),y=getint();
        g[x].push_back(y),g[y].push_back(x);
    }
    dfs(1,0);
    son[root][1]=200001,fa[200001]=root,splay(200001,0);
    m=getint();
    for(int i=1;i<=m;i++)
    {
        op=getint();
        if(op==1)query();
        else if(op==2)modify();
        else extend();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值