[bzoj3729]Gty的游戏

来源:互联网 发布:cnc程式模拟软件 编辑:程序博客网 时间:2024/06/15 14:13

题目大意

给定一颗树,初始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 mindint 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
原创粉丝点击