BZOJ3729 Gty的游戏

转一发blog

首先看这道题的博弈部分,也就是如何得到答案。对于单个节点来说,这就是一个简单的巴什博弈问题,每次可以拿走[1..L]个的话,只需要把石子个数对L+1取模就可以得到它的SG值了。如果考虑树上操作这就是一个阶梯博弈,每次只需要考虑奇数层上的石子。那么只需要对每棵子树维护它的奇数层SG值和偶数层SG值就可以了。因为有插入节点的操作所以dfs序要用Splay来维护。

这道题里面最关键的操作是插入节点的操作。因为访问子树的时候要知道它的入栈点和出栈点,也就是in和out,而如果插入了一个节点就有可能引起很多节点out值的变化。

一个节点的in显然就是它自己,但out值是它子树里最后一个被遍历的节点,也就是它一定是一个叶子节点。那么如果在某个叶子节点下面接了一个节点,就有至少一个节点的out会变化。而我们可以发现这个寻找out的关系是具有传递性的,比如树上某个节点x把u当做它的out,而u当前的out节点指向新接入的儿子v,那么x的out也应该指向v。这就启示我们可以用并查集来维护这个东西,把每个节点并到它的out上去就可以了。

而为了让修改out的次数尽量少,如果接入新节点的那个父亲不是叶子节点,我们可以通过适当处理让所有节点的out都不变,方法就是把新节点的dfs序紧挨着父节点放在它后面。比如当前有dfs序:u-v-w,v和w都是u的儿子,那么显然u的out是w。如果现在还要给u接一个新儿子x,那么我们可以把dfs序修改成:u-x-v-w,这样的话所有已有节点的in和out都不会变。而如果修改成:u-v-w-x,u的out就变了,就造成了不必要的修改。

http://blog.csdn.net/fromatp/article/details/54800000

我们先来看一道POJ1704阶梯博弈题,做法就是将偶的并在一起,奇的话就将1和0并在一起,变成nim博弈。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[1005],n,T;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);int sum=0;
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        for(int i=(n&1)^1;i<=n;i+=2)
        sum=sum^(a[i+1]-a[i]-1);
        if(sum==0)puts("Bob will win");
        else puts("Georgia will win");
    }
}

然后是本题代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int val[N],fa[N],c[N][2],sg[N][2],typ[N],a[N],dep[N],pos[N][2],st[N];
int n,mod,rt,num,tot,m,head[N],ccnt;
map<int,int>id;
struct node
{
    int to,nex;
}e[N];
void add(int x,int y)
{
    e[++ccnt].to=y;e[ccnt].nex=head[x];head[x]=ccnt;
}
void update(int x)
{
    sg[x][0]=sg[c[x][0]][0]^sg[c[x][1]][0];
    sg[x][1]=sg[c[x][0]][1]^sg[c[x][1]][1];
    sg[x][typ[x]]=sg[x][typ[x]]^val[x];
}
void rotate(int x,int &k)
{
    int y=fa[x],z=fa[y],l,r;
    if(c[y][0]==x)l=0;else l=1;r=l^1;
    if(y==k)k=x;
    else {if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;}
    fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
    c[y][l]=c[x][r];c[x][r]=y;
    update(y);update(x);
}
void splay(int x,int &k)
{
    while(x!=k)
    {
        int y=fa[x],z=fa[y];
        if(y!=k)
        {
            if(c[y][0]==x^c[z][0]==y)rotate(x,k);
            else rotate(y,k);
        }
        rotate(x,k);
    }
}
void query(int x)
{
    splay(pos[x][0],rt);
    splay(pos[x][1],c[rt][1]);
    if(sg[c[pos[x][1]][0]][dep[x]^1])
    {
        num++;puts("MeiZ");
    }
    else puts("GTY");
}
void build(int l,int r,int f)
{
    if(l>r)return;
    int mid=l+r>>1;
    c[f][mid>=f]=mid;fa[mid]=f;
    pos[abs(st[mid])][st[mid]<0]=mid;
    if(st[mid]>0)val[mid]=a[st[mid]];
    typ[mid]=dep[abs(st[mid])];
    build(l,mid-1,mid);build(mid+1,r,mid);
    update(mid);
}
void dfs(int x)
{
    st[++tot]=x;
    for(int i=head[x];i;i=e[i].nex)
    {
        int y=e[i].to;
        dep[y]=dep[x]^1;
        dfs(y);
    }
    st[++tot]=-x;
}
void insert(int x,int y,int w)
{
    splay(pos[x][0],rt);
    int k=c[rt][1];while(c[k][0])k=c[k][0];
    int t1=++tot,t2=++tot;
    pos[y][0]=t1;pos[y][1]=t2;fa[t1]=k;fa[t2]=t1;
    dep[y]=dep[x]^1;c[k][0]=t1;c[t1][1]=t2;val[t1]=w;
    typ[t1]=typ[t2]=dep[y];
    update(t2);update(t1);update(k);splay(t2,rt);
}
int main()
{
    scanf("%d%d",&n,&mod);mod++;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);a[i]%=mod;
        id[i]=i;
    }
    int x,y,f,z;
    for(int i=1;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    dfs(1);build(1,tot,0);rt=1+tot>>1;
    scanf("%d",&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d",&f);
        if(f==1){
            scanf("%d",&x);x=x^num;
            query(id[x]);
        }
        else if(f==2){
            scanf("%d%d",&x,&y);x^=num;y^=num;y%=mod;
            splay(pos[id[x]][0],rt);val[rt]=y;update(rt);
        }
        else{
            scanf("%d%d%d",&x,&y,&z);
            x^=num;y^=num;z^=num;z%=mod;id[y]=++n;
            insert(id[x],n,z);
        }
    }
    return 0;
}

 

posted @ 2018-01-16 16:59  大奕哥&VANE  阅读(305)  评论(0编辑  收藏  举报