【模板】LCT

\(LCT\) 是解决动态树问题的一种强力的数据结构,这种数据结构维护的是由若干 \(splay\) 节点构成的森林。
\(LCT\) 结构中采用了实链剖分的策略,即:将树边划分为实边和虚边,其中实边指的是 \(splay\) 节点通过节点中儿子指针相连的边,虚边指的是通过父节点指针相连的边。实边所构成的所有点在同一棵 \(splay\) 中,虚边连接不同的 \(splay\)。这样,就将原树的边集划分到了若干 \(splay\) 中。
对于 \(LCT\) 来说,需要明确一些概念,如下:

  • 原树本身可能不连通,即:真正的树本身就是一个森林。
  • \(splay\) 维护的是每一个联通块中的树的路径
  • \(access\) 操作的作用是使得当前节点与当前联通块的根节点在同一棵 \(splay\)
  • \(find\_root\) 操作是找到当前节点所在的联通块中根节点的编号
  • 单点修改值时,需要将被修改节点旋转到所在 \(splay\) 的根节点,否则需要像线段树一样修改一条链。
#include <bits/stdc++.h>

using namespace std;

struct node {
    node *l, *r, *p;
    int val, sum, rev;
    node(int _val) : val(_val), sum(_val) {
        l = r = p = NULL;
        rev = 0;    
    }
    void unsafe_reverse() {
        swap(l, r);
        rev ^= 1;
    }
    void pull() {
        sum = val;
        if (l != NULL) {
            l->p = this;
            sum ^= l->sum;
        }
        if (r != NULL) {
            r->p = this;
            sum ^= r->sum;
        }
    }
    void push() {
        if (rev) {
            if (l != NULL) {
                l->unsafe_reverse();
            }
            if (r != NULL) {
                r->unsafe_reverse();
            }
            rev = 0;
        }
    }
};
bool is_root(node *v) {
    return v->p == NULL || (v->p->l != v && v->p->r != v);
}
void rotate(node *v) {
    node *u = v->p;
    assert(u != NULL);
    v->p = u->p;
    if (v->p != NULL) {
        if (v->p->l == u) {
            v->p->l = v;
        }
        if (v->p->r == u) {
            v->p->r = v;
        }
    }
    if (v == u->r) {
        u->r = v->l;
        v->l = u;
    }
    if (v == u->l) {
        u->l = v->r;
        v->r = u;
    }
    u->pull();
    v->pull();
}
void deal_with_push(node *v) {
    static stack<node*> stk;
    while (1) {
        stk.push(v);
        if (is_root(v)) {
            break;
        }
        v = v->p;
    }
    while (!stk.empty()) {
        stk.top()->push();
        stk.pop();
    }
}
void splay(node *v) {
    deal_with_push(v);
    while (!is_root(v)) {
        node *u = v->p;
        if (!is_root(u)) {
            if ((v == u->l) ^ (u == u->p->l)) {
                rotate(v);
            } else {
                rotate(u);
            }
        }
        rotate(v);
    }
}
void access(node *v) {
    node *u = NULL;
    while (v != NULL) {
        splay(v);
        v->r = u;
        v->pull();
        u = v;
        v = v->p;
    }
}
void make_root(node *v) {
    access(v);
    splay(v);
    v->unsafe_reverse();
}
node* find_root(node *v) {
    access(v);
    splay(v);
    while (v->l != NULL) {
        v->push();
        v = v->l;
    }
    splay(v);
    return v;
}
void split(node *v, node *u) {
    make_root(v);
    access(u);
    splay(u);
}
void link(node *v, node *u) {
    if (find_root(v) == find_root(u)) {
        return;
    }
    make_root(v);
    v->p = u;
}
void cut(node *v, node *u) {
    make_root(v);
    if (find_root(u) == v && u->p == v && u->l == NULL) {
        u->p = v->r = NULL;
        v->pull();
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    vector<node*> t(n + 1);
    for (int i = 1, val; i <= n; i++) {
        cin >> val;
        t[i] = new node(val);
    }
    while (m--) {
        int opt, x, y;
        cin >> opt >> x >> y;
        if (opt == 0) {
            split(t[x], t[y]);
            cout << t[y]->sum << endl;
        }
        if (opt == 1) {
            link(t[x], t[y]);
        }
        if (opt == 2) {
            cut(t[x], t[y]);
        }
        if (opt == 3) {
            splay(t[x]);
            t[x]->val = y;
            t[x]->pull();
        }
    }
    for (int i = 1; i <= n; i++) {
        delete t[i];
    }
    return 0;
}

转载于:https://www.cnblogs.com/wzj-xhjbk/p/11546219.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值