【模板】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;
}