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

学习博客

https://www.cnblogs.com/flashhu/p/8324551.html


题目

在这里插入图片描述


代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, k;

namespace LCT { // lct板子
    int v[N];// 点权
    int f[N];// 父节点

    int st[N];//栈

    int c[N][2];// 每个节点的两个儿子

    // 判断节点x是否为一个splay的根
    inline bool nroot(register int x) {
    	// 如果节点x是根 则其与其父亲节点之间连的是轻边
        // 如果连的是轻边 他的父亲的儿子里没有它
        return c[f[x]][0] == x || c[f[x]][1] == x;
    }

    int r[N];//r为区间翻转懒标记数组
#define lc c[x][0]
#define rc c[x][1]

    // splay区间翻转操作
    inline void pushr(register int x) {
        register int t = lc;
        lc = rc;
        rc = t;
        r[x] ^= 1;
    }

    // 判断并释放懒标记
    inline void pushDown(register int x) {
        if (r[x]) {
            if (lc) pushr(lc);
            if (rc) pushr(rc);
            r[x] = 0;
        }
    }

    int s[N];

    // 上传信息
    inline void pushUp(register int x) {
        s[x] = s[lc] ^ s[rc] ^ v[x];
    }

    // 一次旋转
    inline void rotate(register int x) {
        register int y = f[x], z = f[y], k = (c[y][1] == x), w = c[x][!k];
        if (nroot(y))
            c[z][c[z][1] == y] = x;
        c[x][!k] = y;
        c[y][k] = w;

        if (w)
            f[w] = y;
        f[y] = x;
        f[x] = z;
        pushUp(y);
    }

    inline void splay(register int x) {
        register int y = x, z = 0;
        st[++z] = y;//暂存当前点到根的整条路径 pushdown时一定要从上往下放标记
        while (nroot(y)) st[++z] = y = f[y];
        while (z) pushDown(st[z--]);
        while (nroot(x)) {
            y = f[x];
            z = f[y];
            if (nroot(y))
                rotate((c[y][0] == x) ^ (c[z][0] == y) ? x : y);
            rotate(x);
        }
        pushUp(x);
    }


    // 打通当前根节点到指定节点的树链
    // 使得一条中序遍历以根开始、以指定点结束的splay出现
    inline void access(register int x) {
        for (int y = 0; x; y = x, x = f[x]) {
            splay(x);
            rc = y;
            pushUp(x);
        }
    }

    //换根
    inline void makeRoot(register int x) {
        access(x);
        splay(x);
        pushr(x);
    }

    //提取路径 拉出x->y的路径成为一个splay 然后以y为splay的根
    inline void split(register int x, register int y) {
        makeRoot(x);
        access(y);
        splay(y);
    }

    // 查找在真实的树中存在的根
    int findRoot(register int x) {
        access(x);
        splay(x);
        while (lc) {
            pushDown(x);
            x = lc;
        }
        splay(x);
        return x;
    }

    // 连边
    inline void link(register int x, register int y) {
        makeRoot(x);
        if (findRoot(y) != x) {
            f[x] = y;
        };
    }


    // 断边
    inline void cut(register int x, register int y) {
        makeRoot(x);
        if (findRoot(y) == x && f[y] == x && !c[y][0]) {
            f[y] = c[x][1] = 0;
            pushUp(x);
        }
    }
}
using namespace LCT;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin);
    freopen("debug.out", "w", stdout);
#endif
    
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    int op, x, y;
    while (m--) {
        cin >> op >> x >> y;
        if (op == 0) { // 询问 (x->y)所有点权xor和
            split(x, y);
            printf("%d\n", s[y]);

        } else if (op == 1) { //连通x->y
            link(x, y);

        } else if (op == 2) { // 删除x->y这条边
            cut(x, y);
        } else {//op=3 将x点权变为y
            // 每次修改值的时候 需要将x转上来
            splay(x);
            v[x] = y;
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值