BZOJ3729: Gty的游戏(伪ETT)

题面

传送门

前置芝士

巴什博奕

\(Nim\)游戏的改版,我们现在每次最多只能取走\(k\)个石子,那么\(SG\)函数很容易写出来

\[SG(x)=mex_{i=1}^{\min(x,k)}SG(x-i)\]

\(SG(0)=0\),用归纳法易知\(SG(x)=x\bmod (k+1)\)

阶梯博弈

\(n\)级台阶,从\(0\)级开始数到\(n\)级。每级上都有一定的石子。每次可以把一个阶梯的石子往下移,\(0\)级阶梯的不能移,不能操作者输。

这里有一个结论,我们只需要考虑所有奇数层,该游戏就等价于\(Nim\)游戏

为啥嘞?

首先偶数层是不会有任何影响的,因为如果你移动偶数层若干个石子到奇数层,那么对手一定可以通过移动把你移的石子移到下一个偶数层,相当于这些石子仍然在偶数层。如果移动奇数层的石子,我们就默认它消失了

题解

那么这题就比较明显了,首先很容易算出每个节点的\(SG\)值,那么对于一个节点\(u\),如果它的深度是奇数,那么答案就是它子树里所有深度为偶数的节点的\(SG\)值的异或和之和,如果不为\(0\)说明妹子赢,否则\(gty\)

于是我们需要在线维护子树深度为奇数的节点的异或和以及深度为偶数的节点的异或和(代码里是维护子树总的异或和和深度为奇数的节点的异或和),并且还要资瓷插入节点

那么用\(ETT\)就好了(也就是\(Splay\)维护\(dfs\)序),而且这里只需要单括号欧拉序就可以了

不过这里有个问题是单括号欧拉序我该怎么找子树代表的区间呢?

如果直接维护\(size\)应该可以做,但是\(size\)还需要链修改非常麻烦。我们可以在\(Splay\)上二分,对于节点\(u\),找到\(dfs\)序比它大的节点中最小的满足\(dep[v]\leq dep[u]\)的节点\(v\),那么\([u,v)\)这个范围就是子树的范围了。同时为了避免\(1\)找不到对应的\(v\),要插入一个虚拟节点,深度设为\(0\),且\(dfs\)序最大

//minamoto
#include<bits/stdc++.h>
#define R register
#define inf 0x3f3f3f3f
#define inline __inline__ __attribute__((always_inline))
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=E[i].v;i;i=E[i].nx,v=E[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
inline int getop(){R char ch;while((ch=getc())>'9'||ch<'0');return ch-'0';}
const int N=1e5+5;
inline int min(R int x,R int y){return x<y?x:y;}
struct eg{int v,nx;}E[N<<1];int head[N],tot;
inline void add(R int u,R int v){E[++tot]={v,head[u]},head[u]=tot;}
struct node;typedef node* ptr;
struct node{
    ptr lc,rc,fa;int sum,s,d,v,mn;
    inline node();
    inline void init(R int ss,R int dd){mn=d=dd,sum=v=ss,s=ss*(d&1);}
    inline ptr upd(){
        mn=min(d,min(lc->mn,rc->mn)),
        sum=lc->sum^rc->sum^v,
        s=lc->s^rc->s^(v*(d&1));
        return this;
    }
}e[N],*rt;
inline node::node(){lc=rc=fa=e;}
void rotate(ptr &rt,ptr p){
    ptr s=p->fa,t=s->fa;
    if(s!=rt)(t->lc==s?t->lc:t->rc)=p;else rt=p;
    s->fa=p,p->fa=t;
    if(s->lc==p)s->lc=p->rc,p->rc->fa=s,p->rc=s->upd();
        else s->rc=p->lc,p->lc->fa=s,p->lc=s->upd();
}
void splay(ptr &rt,ptr p){
    while(p!=rt){
        if(p->fa!=rt)rotate(rt,p->fa->lc==p^p->fa->fa->lc==p->fa?p:p->fa);
        rotate(rt,p);
    }
    p->upd();
}
ptr get(ptr p,int d){
    while(true){
        if(p->lc->mn<=d)p=p->lc;
        else if(p->d<=d)return p;
        else p=p->rc;
    }
}
int n,m,q,tim,cnt,res,dep[N],dfn[N],rk[N],a[N];
void dfs(int u,int fa){
    dfn[u]=++tim,dep[u]=dep[fa]+1,rk[tim]=u;
    go(u)if(v!=fa)dfs(v,u);
}
void build(ptr &p,int l,int r,ptr fa){
    int mid=(l+r)>>1;p=e+rk[mid],p->fa=fa,p->init(a[rk[mid]],dep[rk[mid]]);
    if(l<mid)build(p->lc,l,mid-1,p);
    if(mid<r)build(p->rc,mid+1,r,p);
    p->upd();
}
int main(){
//  freopen("testdata.in","r",stdin);
//  freopen("testdata.out","w",stdout);
    n=read(),m=read(),e->d=e->mn=inf;
    fp(i,1,n)a[i]=read()%(m+1);
    for(R int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u);
    dfs(1,0),build(rt,1,tim,e);
    splay(rt,e+rk[tim]),rt->rc=(e+N-1),
    (e+N-1)->init(0,0),(e+N-1)->fa=rt,rt->upd();
    char op;int u,v,x;q=read();
    ptr s,t;
    while(q--){
        op=read();
        switch(op){
            case 1:{
                u=read()^cnt,s=e+u,splay(rt,s),
                t=get(s->rc,dep[u]);
                splay(s->rc,t);
                res=dep[u]&1?t->lc->s^t->lc->sum:t->lc->s;
                puts(res?(++cnt,"MeiZ"):"GTY");
                break;
            }
            case 2:{
                u=read()^cnt,v=read()^cnt,s=e+u,splay(rt,s);
                s->v=v%(m+1),s->upd();
                break;
            }
            case 3:{
                u=read()^cnt,v=read()^cnt,x=read()^cnt,s=e+u,t=e+v;
                dep[v]=dep[u]+1,a[v]=x%(m+1),t->init(a[v],dep[v]);
                splay(rt,s),s->rc->fa=t,t->rc=s->rc,t->fa=s,s->rc=t;
                t->upd(),s->upd();
                break;
            }
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10716560.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值