BZOJ3282: Tree 题解

Description

给定N个点以及每个点的权值,要你处理接下来的M个操作。
操作有4种。操作从0到3编号。点从1到N编号。
0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。
保证x到y是联通的。
1:后接两个整数(x,y),代表连接x到y,若x到Y已经联通则无需连接。
2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在。
3:后接两个整数(x,y),代表将点X上的权值变成Y。

Input

第1行两个整数,分别为N和M,代表点数和操作数。
第2行到第N+1行,每行一个整数,整数在[ 1,109 1 , 10 9 ]内,代表每个点的权值。
第N+2行到第N+M+1行,每行三个整数,分别代表操作类型和操作所需的量。
1N,M300000 1 ≤ N , M ≤ 300000

Output

对于每一个0号操作,你须输出X到Y的路径上点权的Xor和。

Sample Input

3 3
1
2
3
1 1 2
0 1 2
0 1 1

Sample Output

3


LCT的模板题,学splay和LCT合起来花了两三天
LCT的核心就是用splay维护preferred path,preferred path定义为上次access操作所经过的那些边,access操作的本质是打通当前点到树的根
这题要求删边,加边和维护一条链上的xor和
对于加边(u,v):makeroot(u),fa[u]=v即可,注意虽然这时fa[u]=v,但是u并不是v的儿子,u和v本质上属于两棵splay
对于删边(u,v):makeroot(u),access(v),splay(v),此时u一定是v的左儿子,断掉就行
对于查询(u,v):makeroot(u),access(v),splay(v),这时u是原树的根,v是splay的根,且因为我刚刚做过access操作,所以v所在的splay一定是u到v的路径上的点组成的splay,直接输出sum(v)即可


几个代码易错点:
1. rotate操作和纯splay的rotate操作有一点小小的不一样,LCT的splay中如果y是根,那么不能把z的儿子置为x,因为此时z和x不在一棵splay中,在纯splay中如果y是根,z必然是0,没有这个锅
2. splay操作和纯splay的splay操作有一点小小的不一样
LCT的splay操作判断根的方式是看父亲为0或父亲的孩子不是自己,纯splay只要看父亲是不是0就行
LCT的splay操作中如果开始旋转的点就是当前splay的根,要记得先pushdown一下,因为后面会有其他的splay接到这个点下面,不提前下传标记就混乱了,纯splay中没有这种操作

#include <bits/stdc++.h>
using namespace std;

#define LL long long
#define LB long double
#define ull unsigned long long
#define x first
#define y second
#define pb push_back
#define pf push_front
#define mp make_pair
#define Pair pair<int,int>
#define pLL pair<LL,LL>
#define pii pair<double,double>

const int INF=2e9;
const LL LINF=2e16;
const int magic=348;
const int MOD=998244353;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    bool f;char ch;int res;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

const int MAXN=3e5;

namespace LCT
{
    struct node
    {
        int ch[2],val,father;
        int flip,xsum;
    }tree[MAXN*2];int tot;
    inline void Create(int lson,int rson,int val,int father)
    {
        ++tot;
        tree[tot].ch[0]=lson;tree[tot].ch[1]=rson;
        tree[tot].val=val;tree[tot].father=father;
        tree[tot].flip=0;tree[tot].xsum=val;
    }
    inline void pushup(int cur)
    {
        tree[cur].xsum=(tree[cur].val^tree[tree[cur].ch[0]].xsum^tree[tree[cur].ch[1]].xsum);
    }
    inline void pushdown(int cur)
    {
        if (tree[cur].flip)
        {
            tree[tree[cur].ch[0]].flip^=1;
            tree[tree[cur].ch[1]].flip^=1;
            swap(tree[cur].ch[0],tree[cur].ch[1]);
            tree[cur].flip=0;
        }
    }
    inline bool isroot(int cur) {return !tree[cur].father || (tree[tree[cur].father].ch[0]!=cur && tree[tree[cur].father].ch[1]!=cur);}
    inline void rotate(int x)
    {
        int y=tree[x].father,z=tree[y].father;
        pushdown(y);pushdown(x);
        int k=(tree[y].ch[1]==x);
        if (!isroot(y)) tree[z].ch[tree[z].ch[1]==y]=x;
        tree[y].ch[k]=tree[x].ch[k^1];
        tree[x].ch[k^1]=y;
        tree[tree[y].ch[k]].father=y;tree[y].father=x;tree[x].father=z;
        pushup(y);pushup(x);
    }
    inline void splay(int x)
    {
        pushdown(x);
        while (!isroot(x))
        {
            int y=tree[x].father,z=tree[y].father;
            if (!isroot(y))
                ((tree[y].ch[1]==x)^(tree[z].ch[1]==y))?rotate(x):rotate(y);
            rotate(x);
        }
    }
    inline void access(int cur)
    {
        for (int pre=0;cur;pre=cur,cur=tree[cur].father)
        {
            splay(cur);
            tree[cur].ch[1]=pre;
            pushup(cur);
        }
    }
    inline void makeroot(int x)
    {
        access(x);splay(x);
        tree[x].flip^=1;
    }
    inline int find(int x)
    {
        access(x);splay(x);
        while (tree[x].ch[0]) x=tree[x].ch[0];
        return x;
    }
    inline void link(int u,int v) // u is v's son
    {
        makeroot(u);
        tree[u].father=v;
    }
    inline void cut(int u,int v)
    {
        makeroot(u);
        access(v);splay(v);
        if (tree[v].ch[0]==u)
            tree[v].ch[0]=0,tree[u].father=0;
    }
    inline void update(int x,int val)
    {
        makeroot(x);
        tree[x].val=val;
        pushup(x);
    }
    inline int query(int u,int v)
    {
        makeroot(u);
        access(v);splay(v);
        return tree[v].xsum;
    }
}

int n,q;
int a[MAXN+48];

int main ()
{
    int i,op,x,y;n=getint();q=getint();
    for (i=1;i<=n;i++) a[i]=getint(),LCT::Create(0,0,a[i],0); 
    while (q--)
    {
        op=getint();x=getint();y=getint();
        if (op==0) printf("%d\n",LCT::query(x,y));
        if (op==1) LCT::link(x,y);
        if (op==2) LCT::cut(x,y);
        if (op==3) LCT::update(x,y);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值