P3690 【模板】Link Cut Tree (动态树)

模板链接:https://www.luogu.com.cn/problem/P3690

给定 n 个点以及每个点的权值,要你处理接下来的 m 个操作。
操作有四种,操作从 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。

#include <bits/stdc++.h>

#define rep(i, a, b) for(int i=(int)a;i<=(int)b;i++)
#define pb push_back
#define fi first
#define se second
#define debug(x) cerr<<#x<<"="<<x<<endl;
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 300005;
const int maxm = 60005;
const int inf = 0x3f3f3f3f;
int a[maxn], n, m;

struct Link_Cut_Tree {
    int Top, Son[maxn][2], fa[maxn], W[maxn], Q[maxn], laz[maxn];

    int is_rightson(int x) {
        return x == Son[fa[x]][1];
    }

    //儿子更新后要对父亲进行更新
    void push_up(int rt) {
        W[rt] = W[Son[rt][1]] ^ W[Son[rt][0]] ^ a[rt];
    }

    //延迟标记向下更新
    void push_down(int rt) {
        int &L = Son[rt][0], &R = Son[rt][1];
        if (laz[rt]) {
            laz[rt] ^= 1, laz[L] ^= 1, laz[R] ^= 1;
            swap(L, R);
        }
    }

    //因为Splay的树根的父亲并不记录这个节点(连向该节点的为轻链)
    bool is_root(int rt) {
        return Son[fa[rt]][0] != rt && Son[fa[rt]][1] != rt;
    }

    //将x变成其父亲的父节点
    void Rotate(int rt) {
        int f = fa[rt], ff = fa[f], L, R;
        R = (L = is_rightson(rt)) ^ 1;
        if (!is_root(f)) {
            //如果f不是根节点,那么ff的儿子f会变成rt
            Son[ff][Son[ff][1] == f] = rt;
        }
        fa[rt] = ff, fa[f] = rt, fa[Son[rt][R]] = f;
        Son[f][L] = Son[rt][R], Son[rt][R] = f;
        push_up(f), push_up(rt);
    }

    void Splay(int rt) {
        Q[Top = 1] = rt;
        for (int i = rt; !is_root(i); i = fa[i]) {
            Q[++Top] = fa[i];
        }
        for (int i = Top; i > 0; i--) {
            push_down(Q[i]);
        }
        //将x旋转为根
        while (!is_root(rt)) {
            int f = fa[rt], ff = fa[f];
            if (!is_root(f)) (Son[f][0] == rt) ^ (Son[ff][0] == f) ? Rotate(rt) : Rotate(f);
            Rotate(rt);
        }
    }

    void Access(int rt) {
        //向根连接一条重链
        for (int i = 0; rt; i = rt, rt = fa[rt]) {
            Splay(rt), Son[rt][1] = i, push_up(rt);
        }
    }

    //把rt变成根
    void make_root(int rt) {
        Access(rt), Splay(rt);
        laz[rt] ^= 1;
    }

    //找rt根节点
    int find_root(int rt) {
        Access(rt), Splay(rt);
        while (Son[rt][0]) {
            rt = Son[rt][0];
        }
        return rt;
    }

    //拉出一条x到y的路径为一个Splay
    void Split(int x, int y) {
        make_root(x), Access(y), Splay(y);
    }

    //删除一条x到y的边
    void cut_edge(int x, int y) {
        Split(x, y);
        if (Son[x][1] || fa[x] != y || Son[y][is_rightson(x) ^ 1]) return;
        Son[y][0] = fa[x] = 0;
    }

    //连一条x到y的轻边
    void link_light_edge(int x, int y) {
        make_root(x);
        fa[x] = y;
    }
} LCT;

int read() {
    int ans = 0;
    char last = ' ', ch = getchar();
    while (ch < '0' || ch > '9')last = ch, ch = getchar();
    while (ch >= '0' && ch <= '9')ans = ans * 10 + ch - '0', ch = getchar();
    if (last == '-')ans = -ans;
    return ans;
}

int main() {
#ifdef LOCAL_JUDGE
    freopen("in.txt", "r+", stdin);
#endif
    n = read(), m = read();
    rep(i, 1, n) {
        a[i] = read();
        LCT.W[i] = a[i];
    }
    while (m--) {
        int op = read(), x = read(), y = read();
        if (op == 0) {
            LCT.Split(x, y);
            printf("%d\n", LCT.W[y]);
        } else if (op == 1) {
            if (LCT.find_root(x) != LCT.find_root(y)) {
                LCT.link_light_edge(x, y);
            }
        } else if (op == 2) {
            if (LCT.find_root(x) == LCT.find_root(y)) {
                LCT.cut_edge(x, y);
            }
        } else if (op == 3) {
            a[x] = y;
            LCT.Access(x), LCT.Splay(x), LCT.push_up(x);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值