bzoj4202 石子游戏

题面

题解

可以发现,我们只需考虑离根节点距离为奇数的节点的权值。
因为如果距离为偶数,则后手可以重复先手的步骤。
由于要求每次只能取不超过m个节点,于是我们的权值需要对(m+1)取模。
每次是否只能对同一节点上的石子进行操作,这个题意并没有交代清楚,但是既然AC代码都是取的异或,那说明应该的确每次只能动一个节点。
由于节点的权值以及树的形状会发生变化,又强制在线,所以我们采用LCT维护。每个节点记录子树中深度为奇数/偶数的点的权值异或和。有的题解开了两棵动态树,分别记录奇数,偶数节点信息;其实开一棵就行了,记录两套权值即可。
有几个细节问题:

  • 不用makeroot,也不用打rev标记。
  • 询问深度为奇数/偶数的节点时,查看子树中偶数/奇数深度节点的信息。这样得到的才是到当前节点距离为奇数的节点的异或和.

代码

很丑!!!

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 60000
void _r(int& x)
{
    char c=getchar();
    while(c<'0'||c>'9')
    {
        c=getchar();
    }
    for(x=0;c>='0'&&c<='9';c=getchar())
    {
        x=(x<<1)+(x<<3)+c-'0';
    }
    return ;
}
int n,m,a[MAXN],cnt,Q;
int dep[MAXN],fa[MAXN],ch[MAXN][2],v1[MAXN],v2[MAXN],t1[MAXN],t2[MAXN];
int st[MAXN],top;
int Next[MAXN*2],End[MAXN*2],Last[MAXN],e=0;
void add(int x,int y)
{
    End[++e]=y;
    Next[e]=Last[x];
    Last[x]=e;
    return ;
}
inline bool notroot(int x)
{
    return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void zg(int x,int d)
{
    int y=fa[x],z=fa[y];
    if(notroot(y))
    {
        if(ch[z][0]==y)
        {
            ch[z][0]=x;
        }
        else
        {
            ch[z][1]=x;
        }
    }
    fa[x]=z;
    ch[y][d]=ch[x][!d];
    fa[ch[y][d]]=y;
    ch[x][!d]=y;
    fa[y]=x;
    return ;
}
void push(int p)
{
    if(t1[p])
    {
        v1[p]^=t1[p];
        t1[ch[p][0]]^=t1[p];
        t1[ch[p][1]]^=t1[p];
        t1[p]=0;
    }
    if(t2[p])
    {
        v2[p]^=t2[p];
        t2[ch[p][0]]^=t2[p];
        t2[ch[p][1]]^=t2[p];
        t2[p]=0;
    }
    return ;
}
void splay(int x)
{
    st[top=1]=x;
    for(int i=x;notroot(i);i=fa[i])
    {
        st[++top]=fa[i];
    }
    for(;top;--top)
    {
        push(st[top]);
    }
    int y,z;
    while(notroot(x))
    {
        y=fa[x];
        z=fa[y];
        if(notroot(y))
        {
            if(ch[z][0]==y^ch[y][0]==x)
            {
                zg(x,ch[fa[x]][1]==x);
            }
            else
            {
                zg(y,ch[fa[y]][1]==y);
            }
        }
        zg(x,ch[fa[x]][1]==x);
    }
    return ;
}
void access(int x)
{
    for(int t=0;x;t=x,x=fa[x])
    {
        splay(x);
        ch[x][1]=t;
    }
    return ;
}
void rt(int x)
{
    access(x);
    splay(x);
    return ;
}
inline void link(int x,int y)
{
    fa[x]=y;
    return ;
}
void change(int x,int p,int d)
{
    rt(1);
    rt(x);
    if(p)
    {
        t1[x]^=d;
    }
    else
    {
        t2[x]^=d;
    }
    return ;
}
int query(int x,int p)
{
    rt(1);
    rt(x);
    if(p)
    {
        return v1[x];
    }
    else
    {
        return v2[x];
    }
}
void dfs(int x,int ff)
{
    dep[x]=dep[ff]+1;
    for(int t=Last[x],y;t;t=Next[t])
    {
        y=End[t];
        if(y!=ff)
        {
            link(y,x);
            dfs(y,x);
        }
    }
    return ;
}
int main_main()
{
    //freopen("r.cpp","r",stdin);
    _r(n);
    _r(m);
    for(int i=1;i<=n;i++)
    {
        _r(a[i]);
        a[i]%=(m+1);
    }
    for(int i=1,x,y;i<n;i++)
    {
        _r(x);
        _r(y);
        add(x,y);
        add(y,x);
    }
    dep[0]=-1;
    dfs(1,0);
    for(int i=1;i<=n;i++)
    {
        if(dep[i]&1)
        {
            change(i,1,a[i]);
        }
        else
        {
            change(i,0,a[i]);
        }
    }
    _r(Q);
    cnt=0;
    for(int i=1,ty,u,v,x;i<=Q;i++)
    {
        _r(ty);
        if(ty==1)
        {
            _r(x);
            x^=cnt;
            int t;
            if(dep[x]&1)
            {
                t=query(x,0);
            }
            else
            {
                t=query(x,1);
            }
            if(t)
            {
                puts("Yes");
                ++cnt;
            }
            else
            {
                puts("No");
            }
        }
        if(ty==2)
        {
            _r(u);
            _r(v);
            u^=cnt;
            v^=cnt;
            v%=(m+1);
            if(dep[u]&1)
            {
                change(u,1,a[u]);
                change(u,1,v);
            }
            else
            {
                change(u,0,a[u]);
                change(u,0,v);
            }
            a[u]=v;
        }
        if(ty==3)
        {
            _r(u);
            _r(v);
            _r(x);
            u^=cnt;
            v^=cnt;
            x^=cnt;
            x%=(m+1);
            dep[v]=dep[u]+1;
            a[v]=x;
            link(v,u);
            if(dep[v]&1)
            {
                change(v,1,a[v]);
            }
            else
            {
                change(v,0,a[v]);
            }
        }
    }
    return 0;
}
const int main_stack=16;  
char my_stack[128<<20];  
int main() {  
  __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");  
  __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");  
  main_main();  
  __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");  
  return 0;  
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值