UOJ207 共价大爷游长沙

Problem

UOJ

Solution

这道题可以维护子树hash值来做。这个hash函数要求仅与各个数的值有关。
如果边(x,y)在所有的路径上,那么我们可以认为在子树x/y中,hash值是要等于某个值的(这个值我们可以维护总的hash值)。而一个比较好的hash就是异或和,为了不冲突我们可以用2的幂来赋值,但注意到路径可能比较多,所以直接rand一个大数,冲突的概率也是比较小的。
而这道题要求要维护子树信息,用s1代表虚儿子的异或和,s2代表总的异或和,然后大概就是板子了。

Code

#include <algorithm>
#include <cstdlib>
#include <cstdio>
#define pd(x) if(rev[x])pushdown(x)
#define pushup(x) s2[x]=s2[ch[x][0]]^s2[ch[x][1]]^s1[x]
#define rg register
using namespace std;
const int maxn=100010;
struct data{int x,y,v;}opr[maxn*3];
int n,m,opt,top,cnt,f[maxn],ch[maxn][2],rev[maxn],s2[maxn],s1[maxn],stk[maxn];
template <typename Tp> inline void read(Tp &x)
{
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
inline int isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void pushdown(int x)
{
    rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;
    swap(ch[x][0],ch[x][1]);rev[x]=0;
}
void rotate(int x)
{
    int fa=f[x],ff=f[fa],l,r;
    if(ch[fa][0]==x) l=0;else l=1;r=l^1;
    if(!isroot(fa)){if(ch[ff][0]==fa) ch[ff][0]=x;else ch[ff][1]=x;}
    f[x]=ff;f[fa]=x;f[ch[x][r]]=fa;
    ch[fa][l]=ch[x][r];ch[x][r]=fa;
    pushup(fa);pushup(x);
}
void splay(int x)
{
    stk[top=1]=x;
    for(int i=x;!isroot(i);i=f[i]) stk[++top]=f[i];
    for(;top;top--) pd(stk[top]);
    while(!isroot(x))
    {
        int fa=f[x],ff=f[fa];
        if(!isroot(fa))
        {
            if((ch[fa][0]==x)^(ch[ff][0]==fa)) rotate(x);
            else rotate(fa);
        }
        rotate(x);
    }
}
void access(int x){for(int t=0;x;t=x,x=f[x]) splay(x),s1[x]^=s2[ch[x][1]]^s2[t],ch[x][1]=t,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int find(int x){access(x);splay(x);while(ch[x][0]) x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);if(ch[y][0]==x) ch[y][0]=f[x]=0;pushup(y);}
void link(int x,int y){makeroot(x);makeroot(y);f[x]=y;s1[y]^=s2[x];pushup(y);}
void update(int x,int v){access(x);splay(x);s2[x]^=v;s1[x]^=v;}
//link中都makeroot以及update中的access+splay是因为懒得向上更新
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    srand(19260817);
    int x,y,v,s=0;
    read(x);read(n);read(m);
    for(rg int i=1;i<n;i++)
    {
        read(x);read(y);
        link(x,y);
    }
    while(m--)
    {
        read(opt);
        if(opt==1){read(x);read(y);cut(x,y);read(x);read(y);link(x,y);}
        else if(opt==2)
        {
            read(x);read(y);v=rand();
            opr[++cnt]=(data){x,y,v};
            s^=v;update(x,v);update(y,v);
        }
        else if(opt==3)
        {
            read(x);s^=opr[x].v;
            update(opr[x].x,opr[x].v);update(opr[x].y,opr[x].v);
        }
        else
        {
            read(x);read(y);split(x,y);
            if(s1[y]==s) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值