[bzoj3729]Gty的游戏

25 篇文章 1 订阅
13 篇文章 0 订阅

题目大意

给定一颗树,初始n个结点,1为根节点。每个结点上有一定的石子数。
现在你需要在线兹瓷三种操作:
1、询问以x为根的子树中进行组合游戏,双方轮流操作,每次操作可以将一个结点(在子树内且不为x)的不超过p个至少1个石子移至其父亲结点。问这个游戏先手是否必胜?
2、修改一个结点的石子数。
3、新建一个结点石子数为x,其父亲设为y(保证y已经建立)

一点姿势

我们需要解决以下两个博弈论问题:
1、nim游戏的改版,每次最多拿走m个石子。
那么 sg[i]=mexmin(m,i)i=1sg[im]
找规律及感性认识得 sg[i]=i%(m+1)
那我们干脆把初始石子数直接模m+1变为普通Nim游戏。
2、有n级台阶,从0级开始数到n级。每级上都有一定得石子。每次可以把一个阶梯的石子往下移,0级阶梯的不能移,不能操作者输。
这个比较机智!结论是:我们只在乎奇数层的石子数,然后把每个奇数层当作一堆石子,那么该游戏等价于nim游戏。
为什么呢?
移动偶数层的石子,我们可以把该层下的石头拿等量继续往下,相当于这些石子还在偶数层。移动奇数层的石子,我们视为它消失了。
于是就是经典nim游戏了。

splay维护dfs序

我们用dfs序来表示这个树。
那么询问操作相当于询问区间深度与x深度奇偶性不同的数的nim和,修改操作就直接改,添加操作就是在一个数后添加一个数。
我们用splay维护dfs序,现在的问题是如何获取询问操作询问的是哪一个区间?
左端点肯定是x,右端点呢?传统做法应该维护size,但那样我们需要兹瓷size的动态维护涉及链修改,这里可以利用splay搞一波。
结论:在最后添加虚拟结点深度设为0,那么对于x,找到其在dfs序右边最近的一个点y满足d[y]<=d[x],那么[x,y)即是询问区间。至于为什么要虚拟结点这个自己yy即可得。
那么用splay维护区间深度为奇数的点nim和、所有点的nim和、所有点深度最小值即可。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
int key[maxn],d[maxn],father[maxn],tree[maxn][2],sum[maxn],num[maxn],cnt[maxn];//sum quan xor num ji xor cnt mind
int h[maxn],go[maxn*2],next[maxn*2],id[maxn];
int i,j,k,l,t,n,m,p,tot,top,root,mask;
void add(int x,int y){
    go[++top]=y;
    next[top]=h[x];
    h[x]=top;
}
void update(int x){
    sum[x]=sum[tree[x][0]]^sum[tree[x][1]]^key[x];
    num[x]=num[tree[x][0]]^num[tree[x][1]];
    if (d[x]%2) num[x]^=key[x];
    cnt[x]=d[x];
    if (tree[x][0]) cnt[x]=min(cnt[x],cnt[tree[x][0]]);
    if (tree[x][1]) cnt[x]=min(cnt[x],cnt[tree[x][1]]);
}
int pd(int x){
    if (x==tree[father[x]][0]) return 0;else return 1;
}
void rotate(int x){
    int y=father[x],z=pd(x);
    father[x]=father[y];
    if (father[y]) tree[father[y]][pd(y)]=x;
    tree[y][z]=tree[x][1-z];
    if (tree[x][1-z]) father[tree[x][1-z]]=y;
    tree[x][1-z]=y;
    father[y]=x;
    update(y);
    update(x);
}
void splay(int x,int y){
    while (father[x]!=y){
        if (father[father[x]]!=y)
            if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
        rotate(x);
    }
}
void dfs(int x,int y){
    if (y) d[x]=d[y]+1;
    tree[root][1]=x;
    father[x]=root;
    update(root);
    splay(x,0);
    root=x;
    int t=h[x];
    while (t){
        if (go[t]!=y) dfs(go[t],x);
        t=next[t];
    }
}
int find(int x,int y){
    if (tree[x][0]&&cnt[tree[x][0]]<=y) return find(tree[x][0],y);
    else if (d[x]<=y) return x;
    else return find(tree[x][1],y);
}
int main(){
    //freopen("gty.in","r",stdin);
    scanf("%d%d",&n,&p);
    fo(i,1,n){
        ll x;
        scanf("%lld",&x);
        id[i]=++tot;
        x%=(p+1);
        key[tot]=x;
    }
    fo(i,1,n-1){
        scanf("%d%d",&j,&k);
        add(j,k);add(k,j);
    }
    dfs(1,0);
    tree[root][1]=++tot;
    father[tot]=root;
    update(root);
    splay(tot,0);
    root=tot;
    scanf("%d",&m);
    mask=0;
    while (m--){
        scanf("%d",&t);
        if (t==1){
            scanf("%d",&j);
            //j^=mask;
            j=id[j];
            splay(j,0);
            root=j;
            k=find(tree[j][1],d[j]);
            splay(k,0);
            root=k;
            splay(j,k);
            if (d[j]%2==0) t=num[tree[j][1]];else t=sum[tree[j][1]]^num[tree[j][1]];
            if (t) printf("MeiZ\n"),mask++;else printf("GTY\n");
        }
        else if (t==2){
            scanf("%d",&j);
            //j^=mask;
            j=id[j];
            ll x;
            scanf("%lld",&x);
            //x^=mask;
            x%=(p+1);
            splay(j,0);
            root=j;
            key[j]=x;
            update(j);
        }
        else{
            scanf("%d%d",&j,&k);
            //j^=mask;k^=mask;
            j=id[j];
            id[k]=++tot;
            k=id[k];
            d[k]=d[j]+1;
            ll x;
            scanf("%lld",&x);
            //x^=mask;
            x%=(p+1);
            key[k]=x;
            splay(j,0);
            root=j;
            l=tree[j][1];
            tree[j][1]=k;
            father[k]=j;
            tree[k][1]=l;
            if (l) father[l]=k;
            update(k);
            update(j);
            /*splay(k,0);
            root=k;*/
        }
    }
}

树上分块大法好

实际上这道题我们是可以树上分块的,然后每块的维护信息相当于上文提到的spaly方法所要维护的信息。
为什么要提分块?别忘了这是gty系列!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值