2019.01.06-dtoj-3729: Gty的游戏

题目描述:

某一天gty在与他的妹子玩游戏。妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问
将某个节点的子树中的石子移动到这个节点先手是否有必胜策略。
gty很快计算出了策略。
但gty的妹子十分机智,她决定修改某个节点的石子或加入某个新节点。
gty不忍心打击妹子,所以他将这个问题交给了你。
另外由于gty十分绅士,所以他将先手让给了妹子。

输入:

第一行两个数字,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。

算法标签:splay,博弈

思路:

首先,对于把石子移到子树的根的问题其实是一个阶梯nim问题,倘若把奇数层的节点异或后值为0,则先手必败,其余必胜。

对于修改操作,用splay维护,在splay中子树的范围确定,从我向后找到第一个深度小于我的点之前的点都是我的子树,其余的都比较好维护。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5;
int head[N],ne[N<<1],to[N<<1],tot,rt;
int n,l,a[N],mnd[N],m,cnt,sum[N][2],d[N],num[N],son[N][2],sz[N],last,fa[N];
il int read(){int x;char ch;_(!);x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return x;}
il void insert(int x,int y){ne[++tot]=head[x];head[x]=tot;to[tot]=y;}
il void dfs(int x,int fa){
    num[++cnt]=x;
    for(int i=head[x];i;i=ne[i]){
        if(fa==to[i])continue;
        d[to[i]]=d[x]+1;dfs(to[i],x);
    }
}
il void update(int x){
    sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
    sum[x][1]=sum[son[x][1]][1]^sum[son[x][0]][1];
    sum[x][0]=sum[son[x][1]][0]^sum[son[x][0]][0];
    sum[x][d[x]&1]^=a[x];mnd[x]=d[x];
    if(son[x][0])mnd[x]=min(mnd[x],mnd[son[x][0]]);
    if(son[x][1])mnd[x]=min(mnd[x],mnd[son[x][1]]);
}
il void build(int &x,int l,int r,int now){
    
    int mid=(l+r)>>1;x=num[mid];fa[x]=now;
    if(l<mid)build(son[x][0],l,mid-1,x);
    if(mid<r)build(son[x][1],mid+1,r,x);
    update(x);
}
il void rotate(int x,int &k){
    int y=fa[x],z=fa[y],tp=son[y][1]==x;
    if(y==k)k=x;else son[z][son[z][1]==y]=x;
    
    son[y][tp]=son[x][tp^1];fa[son[y][tp]]=y;
    son[x][tp^1]=y;fa[y]=x;fa[x]=z;
    update(y);update(x);
    
}
il void splay(int x,int&k){
    while(x!=k){
        int y=fa[x],z=fa[y];
        if(y!=k)rotate(((son[y][1]==x^son[z][1]==y)?x:y),k);
        rotate(x,k);
    }
}
il int find(int x,int k){
    if(!x)return 0;
    if(min(mnd[son[x][0]],d[x])>k)return find(son[x][1],k);
    if(mnd[son[x][0]]>k)return x;
    return find(son[x][0],k);
}
int main()
{
    n=read();l=read();mnd[0]=1e9;
    for(int i=1;i<=n;i++)a[i]=read()%(l+1);
    for(int i=1;i<n;i++){int x=read(),y=read();insert(x,y);insert(y,x);}
    d[1]=1;num[++cnt]=1e5+1;dfs(1,0);num[++cnt]=1e5+2;
    build(rt,1,cnt,0);
    m=read();
    while(m--){
        int op=read();
        if(op==1){
            int x=read()^last;
            splay(x,rt);
            int y=find(son[x][1],d[x]);
            splay(y,son[x][1]);
            int res=sum[son[y][0]][(d[x]&1)==0];
            if(!son[y][0]||!res)puts("No");
            else puts("Yes"),++last;
        }
        else if(op==2){
            int x=read()^last;a[x]=(read()^last)%(l+1);
            for(;x;x=fa[x])update(x);
        }
        else{
            int x=read()^last,z=read()^last,y;
            splay(x,rt);y=son[x][1];while(son[y][0])y=son[y][0];
            a[z]=(read()^last)%(l+1),d[z]=d[x]+1;
            if(!y)fa[z]=x,son[x][1]=z,update(z),update(x);
            else{
                splay(y,son[x][1]);son[y][0]=z;fa[z]=y;
                update(z);update(y);update(x);
            }
        }
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Jessie-/p/10230737.html
今日推荐