【BZOJ3729】Gty的游戏,博弈+splay

传送门
挺蛋疼的一道题
连想带做搞了一晚上晚自习+下午两节课
博弈方面还是很好弄的,就是“阶梯问题”+“Nim取模游戏”
分别维护深度为奇数和偶数的节点的sg函数
然后直接查询就可以了
问题在于如何实时添加新节点
这个难了我好久
聪哥提出利用分块重构+暴力思想,蒟蒻不是很懂
考虑到子树查询多与dfs序有关,想用splay维护一下
然后怎么维护并实时查询每个节点的dfs序成了难题
又难了好久……
后来联想到了ST表维护LCA(???),然后莫名奇妙就想到了把dfs序拆成两个点
因为之前做树链剖分的时候dfs序只是1-n,而这里可以把树上的节点dfs序记录两次,变成1-2n,下面详细说一下
设当前dfs到的节点为x,dfs序的计数器为cnt,代表x的入dfs序为L[x],出dfs序为R[x](为了方便表达,我在代码中将它们编号为2·x-1和2·x),splay中节点i的权值为w[i](splay中按w为关键字进行排序)
节点x进入dfs时cnt++,w[L[x]]=cnt,扫完所有该节点子树,离开dfs时再cnt++,w[R[x]]=cnt
最后把所有的L[x]和R[x]都扔进splay中
这样splay中就有2n个节点了
怎么维护sg函数呢?
再加一个数组b
其中b[L[x]]=a[x],b[R[x]]=0
这样就能解决问题了
操作1
把L[x]的前驱节点旋到根,然后把R[x]的后继旋到根的右儿子,这样根的右儿子的左子树就是所求的范围了

操作2
直接把要改的节点x的L[x]旋到根,直接改就可以了
操作3
设插入节点为y,插入到x的下面,石子数为z
像上面普通的插入节点类似,但是要注意,把L[x]旋到根,然后把根的右子树的w全加2
w[L[y]]=w[L[x]]+1,w[R[y]]=w[L[x]]+2

虽然艰难地A了
还是要吐槽一下
自己实在写的太烂
操作1中不知道为什么我必须先把L[x],R[x]分别旋到根,再进行后续操作才能把lazy标记施放下去
还有就是范围必须开到60000才能过,不然就RE
之前并没有写过splay维护dfs序,这次YY了好久才写出来这个常数大代码长的东西
要是在考场上调这种自己没写过的东西,八成是作大死了
决定等会去看一下别人是怎么写的……
还是要好好学习才行啊~~
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 60005
using namespace std;
int n,m,P,root,tot,cnt;
int a[M],b[M<<1],first[M],fa[M<<1],w[M<<1],son[M<<1]
[2],L[M],R[M],s0[M<<1],s1[M<<1],dep[M],lazy[M<<1],stacks[M<<1];
bool T[M<<1];
struct edge{
    int v,next;
}e[M<<1];
void add(int x,int y)
{
    e[++tot]=(edge){y,first[x]};first[x]=tot;
    e[++tot]=(edge){x,first[y]};first[y]=tot;
}
int in()
{
    int t=0;char ch=getchar();
    while (ch>'9'||ch<'0') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return t;
}
void dfs(int x,int fa)
{
    L[x]=x*2-1;R[x]=x*2;
    w[L[x]]=++cnt;
    T[L[x]]=dep[x];
    if (dep[x]) s1[L[x]]=a[x];
    else s0[L[x]]=a[x];
    for (int i=first[x];i;i=e[i].next)
        if (e[i].v!=fa)
            dep[e[i].v]=!dep[x],
            dfs(e[i].v,x);
    w[R[x]]=++cnt; 
}
void ct(int x)
{
    s0[x]=s1[x]=0;
    if (T[x]) s1[x]^=b[x];
    else s0[x]^=b[x];
    if (son[x][0]!=-1)
        s1[x]^=s1[son[x][0]],
        s0[x]^=s0[son[x][0]];
    if (son[x][1]!=-1)
        s1[x]^=s1[son[x][1]],
        s0[x]^=s0[son[x][1]];
}
void pushdown(int x)
{
    if (!lazy[x]) return;
    if (son[x][0]!=-1)
        lazy[son[x][0]]+=lazy[x],
        w[son[x][0]]+=lazy[x];
    if (son[x][1]!=-1)
        lazy[son[x][1]]+=lazy[x],
        w[son[x][1]]+=lazy[x];
    lazy[x]=0;
}
void rorate(int x,bool f)
{
    int y=fa[x];
    son[y][!f]=son[x][f];
    if (son[x][f]!=-1)fa[son[x][f]]=y;
    fa[x]=fa[y];
    if (fa[y]!=-1)
        if (son[fa[y]][0]==y) son[fa[y]][0]=x;
        else son[fa[y]][1]=x;
    son[x][f]=y;
    fa[y]=x;
    ct(y);ct(x);
}
void splay(int x,int goal)
{
    int t=x;
    stacks[++stacks[0]]=x;
    for (;fa[x]!=goal;)x=fa[x],stacks[++stacks[0]]=x;
    x=t;
    for (;stacks[0];--stacks[0]) pushdown(stacks[stacks[0]]);
    for (int y;fa[x]!=goal;)
    {
        y=fa[x];
        if (fa[y]==goal)
            if (son[y][0]==x) rorate(x,1);
            else rorate(x,0);
        else if (son[fa[y]][0]==y)
        {
            if (son[y][0]==x) rorate(y,1);
            else rorate(x,0);
            rorate(x,1);
        }
        else
        {
            if (son[y][1]==x) rorate(y,0);
            else rorate(x,1);
            rorate(x,0); 
        }
    }
    if (goal==-1) root=x;
}
int Kth(int x)
{
    int now=root;
    for (;;)
    {
        pushdown(now);
        if (w[now]>x)
            if (son[now][0]==-1) return now;
            else now=son[now][0];
        else if (w[now]<x)
            if (son[now][1]==-1) return now;
            else now=son[now][1];
        else return now;
    }
}
void insert(int x)
{
    if (root==-1) return void(root=x);
    int now=root;
    for (;;)
    {
        pushdown(now);
        if (!T[x]) s0[now]^=b[x];
        else s1[now]^=b[x];
        if (w[now]>w[x])
            if (son[now][0]==-1)
                {son[now][0]=x;fa[x]=now;break;}
            else
                now=son[now][0];
        else
            if (son[now][1]==-1)
                {son[now][1]=x;fa[x]=now;break;}
            else
                now=son[now][1];
    }
    splay(x,-1);
}
main()
{
    n=in();P=in()+1;
    for (int i=1;i<=n;++i) a[i]=in()%P;
    for (int i=1;i<n;++i) add(in(),in());
    dfs(1,0);
    memset(son,-1,sizeof(son));
    memset(fa,-1,sizeof(fa));
    root=-1;
    insert(0);
    for (int i=1;i<=n;++i)
        b[L[i]]=a[i],
        insert(L[i]),insert(R[i]);
    w[M*2-4]=M*2-4;
    insert(M*2-4);
    int tp,x,y,z,Mei=0;
    for (m=in();m;--m)
    {
        tp=in();
        if (tp==1)
        {
            x=in()^Mei;
            splay(L[x],-1);
            splay(R[x],-1);
            splay(Kth(w[L[x]]-1),-1);
            splay(Kth(w[R[x]]+1),root);
            y=s0[son[son[root][1]][0]];
            z=s1[son[son[root][1]][0]];
            if ((y&&T[L[x]])||(z&&!T[L[x]])) puts("MeiZ"),++Mei;
            else puts("GTY");
        }
        else if (tp==2)
        {
            x=in()^Mei;y=(in()^Mei)%P;
            splay(L[x],-1);
            if (T[root]) s1[root]^=b[root];
            else s0[root]^=b[root];
            b[root]=y;
            if (T[root]) s1[root]^=b[root];
            else s0[root]^=b[root];
        }
        else
        {
            x=in()^Mei;y=in()^Mei;z=(in()^Mei)%P;
            splay(L[x],-1);
            lazy[son[root][1]]+=2;w[son[root][1]]+=2;
            L[y]=2*y-1,R[y]=2*y;
            w[L[y]]=w[root]+1,w[R[y]]=w[root]+2;
            b[L[y]]=z;
            T[L[y]]=!T[root];
            insert(L[y]);insert(R[y]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值