LCT(link cut tree) -模板

//LCT Link Cut Tree(动态树) 模板
//洛谷 P3690 【模板】Link Cut Tree (动态树)
//2020.11.10 创建
//2020.11.16 结束
//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。

#include <bits/stdc++.h>
#define ll long long
#define _for(i,a,b) for(int i = (a);i < (b); ++i)
#define sc scanf
#define pr printf
#define TLE ios::sync_with_stdio(false); cin.tie(0);
#define swap(x,y) x ^= y, y ^= x, x ^= y
const int maxn = 3e5+10;
const int INF = 0x3f3f3f3f;
using namespace std;

#define fa(x) tree[x].fath
#define ls(x) tree[x].ch[0]
#define rs(x) tree[x].ch[1]

struct Node {
    int fath; //父节点
    int ch[2]; //子节点
    int s; //标记
    bool r; //是否翻转
}tree[maxn];
int n,m;
int w[maxn];

//判断该节点是父亲的哪个儿子
int ident(int x) {
    return ls(fa(x)) == x ? 0 : 1;
}

//连接
void connect(int x,int fa,int how) {
    fa(x) = fa;
    tree[fa].ch[how] = x;
    return;
}

//判断节点是否为Splay的根节点
inline bool IsRoot(int x) {
    return ls(fa(x)) != x && rs(fa(x)) != x;
    //1.若x与fa(x)之间的边是虚边,那么它的父亲的孩子中不会有他(不在同一个splay内)
    //2. splay的根节点与其父亲之间的边是虚边
}

//维护路径异或和
inline void push_up(int x) {
    tree[x].s = tree[ls(x)].s ^ tree[rs(x)].s ^ w[x];
    return;
}

//标记下传
void pushdown(int x) {
    if(tree[x].r) {
        swap(ls(x),rs(x));
        tree[ls(x)].r ^= 1;
        tree[rs(x)].r ^= 1;
        tree[x].r = 0;
    }
}

/*
void rotate(int x) {
    int Y = fa(x), R = fa(Y), Yson = ident(x), Rson = ident(Y);
    int B = tree[x].ch[Yson ^ 1];
    fa(x) = R;
    if(!IsRoot(Y))
        connect(x, R, Rson);
    //这里如果不判断y是否根节点,那么当y是根节点的时候,0节点的儿子就会被更新为x
    //这样x就永远不能被判断为根节点,也就会无限循环下去了
    //但是这里不更新x的父亲的话就会出现无限递归的情况
    connect(B, Y, Yson);
    connect(Y, x, Yson ^ 1);
    push_up(Y); push_up(x);
}
*/
#define id(x,y) tree[x].ch[1] == y

void rotate(int x) { //单旋
    int y = fa(x);
    int z = fa(y);
    int k = id(y,x); // k = 1 则x是y的右儿子
                                // k = 0 则x是y的左儿子
    //这里如果不判断y是否根节点,那么当y是根节点的时候,0节点的儿子就会被更新为x
    //这样x就永远不能被判断为根节点,也就会无限循环下去了
    //但是这里不更新x的父亲的话就会出现无限递归的情况
    if(!IsRoot(y))
        tree[z].ch[id(z,y)] = x; // y < z -> x < z;
    fa(x) = z;                   // y > z -> x < z;
    tree[y].ch[k] = tree[x].ch[k ^ 1]; // 如果当前节点是x的k孩子,旋转之后则必是y的!k孩子
    fa(tree[x].ch[k ^ 1]) = y;
    tree[x].ch[k ^ 1] = y; // 若x是y的左儿子,则y必是x的右儿子,反之同理
    fa(y) = x;
    push_up(y);
    push_up(x);
}

int st[maxn];

void Splay(int x) {
    int now = x, top = 0;
    st[++top] = now;
    while(!IsRoot(now)) st[++top] = now = fa(now);
    while(top) pushdown(st[top--]);
    /*
     for(int now = fa(x); !IsRoot(x); rotate(x), now = fa(x))//只要不是根就转
        if(!IsRoot(now))
            rotate( ident(x) == ident(now) ? x : now );
    */
    while(!IsRoot(x)) {
        int y = fa(x);
        if(!IsRoot(y))
            rotate( (ls(y) == x) ^ (ls(fa(y)) == y) ? x : y );
        rotate(x);
    }
}

//将根节点到x点的路径变为实路径,且x与其儿子之间的边都变为虚边
inline void access(int x) {
    for(int y = 0; x; x = fa(y = x)) {
        Splay(x);
        rs(x) = y;
        push_up(x);
    }
}

//将x变为原树的根
inline void makeroot(int x) {
    access(x);
    Splay(x);
    tree[x].r ^= 1;
    pushdown(x);
}

//找到x所在原树的根
inline int findroot(int x) {
    access(x);
    Splay(x);
    pushdown(x);
    while(ls(x)) pushdown(x = ls(x));
    return x;
}

//获取x->y所对应的路径
inline void split(int x,int y) {
    makeroot(x); //将x置于原树根节点位置
    access(y); //将x->y连成Splay
    Splay(y); //将y变为当前Splay的根节点
}

//连接x,y
inline void link(int x,int y) {
    makeroot(x);
    if(findroot(y) != x) tree[x].fath = y;
}

//断开边x-y
inline void cut(int x,int y) {
    makeroot(x);
    if(findroot(y) == x //x,y在同一棵原树上
       && fa(x) == y //x的父亲为y
       && ls(y) == x //y的左儿子为x
       && !rs(x)) //没有比x深度更大的节点
    {
        fa(x) = ls(y) = 0;
        push_up(y);
    }
}

int main() {
    sc("%d %d",&n,&m);
    _for(i,1,n+1) {
        sc("%d",&w[i]);
    }
    int opt,x,y;
    _for(i,1,m+1) {
        sc("%d %d %d",&opt,&x,&y);
        //0 x y: 代表询问从 x 到 y 的路径上的点的权值的 xor 和。保证 x 到 y 是联通的。
        if(opt == 0) {
            split(x,y);
            pr("%d\n",tree[y].s);
        }
        //1 x y: 代表连接 x 到 y,若 x 到 y 已经联通则无需连接。
        else if(opt == 1) {
            link(x,y);
        }
        //2 x y: 代表删除边(x,y),不保证边(x,y) 存在。
        else if(opt == 2) {
            cut(x,y);
        }
        //3 x y: 代表将点 x 上的权值变成 y。
        else if(opt == 3) {
            Splay(x);
            w[x] = y;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值