阅读前请确保你会( ext{splay})。
( ext{LinkCutTree})模板,简称( ext{lct})。
首先我们把一棵树里所有节点都全部划分在一些( ext{splay})里,每一个( ext{splay})维护的东西都是一条在原树上从上到下按深度递增的一条链。
然后我们定义每条边有两种类型:实或者虚。
其中实边代表的是在某个( ext{splay})内部的边,而虚边则是某一个( ext{splay})所指向另一个节点所对应的边。
然后这就是所谓的“实链剖分”了。而我们大家所熟知的树剖则是“重链剖分”。
然后我们再来看一看各种操作。
( ext{access})操作
这个操作代表的是打通一条根节点到现在节点的实链。
我们可以选择的方法是从下往上拉。
每次让要操作的节点成为当前( ext{splay})的根,然后将它的右子树变成( ext{0}),接着在根据虚边往上拉。
你们可以画一棵树出来,更熟悉的理解一下。
( ext{access})操作是( ext{lct})的基础,其他操作全是在( ext{access})的基础上完成的。
( ext{makeroot})操作。
这个操作就是换根操作。
我们先要知道一点,那就是我们( ext{access})后这个节点一定是当前( ext{splay})中深度最大的节点。(应该容易理解,可以自行思考一下)。
此时我们再让此节点成为当前( ext{splay})的根,那么这个节点一定是没有右子树的。
这个时候我们有一个神奇的思路:
直接把这个节点的左右子树交换,不就成了根了吗?
事实证明这种思路是可行的。
( ext{findroot})操作
找( ext{x})所在的树中的根。
我们先( ext{access})一次,然后再( ext{splay})一次。
此时的所有点都在当前节点的左子树里,毫无疑问,原来的树根就是目前为止一直左子树到最后一个的点。
( ext{split})操作
这个操作的含义定义为拉出一条从( ext{x})到( ext{y})的链。
我们先( ext{makeroot(x)}),然后( ext{access(y)}),此时由于( ext{x})是根节点,所以( ext{x})和( ext{y})就处在了同一个( ext{splay})中。
这时我们再将( ext{y})变成现在( ext{splay})里的根就好了。
( ext{link})
操作
定义含义为连接某一条边。
我们发现( ext{makeroot})之后由于是根节点,这个节点是没有父亲的,我们直接将这个节点的父亲赋为( ext{y})就好了。
( ext{cut})操作
定义为删掉( ext{x})到( ext{y})的边
我们首先拉出一条从( ext{x})到( ext{y})的链。
一般来说( ext{cut})的时候一定是存在( ext{x})到( ext{y})的边的,但是为了以防万一,这种特殊情况也要处理一下。
有几种情况:
1:( ext{y})不仅有( ext{x})一个儿子。
2:( ext{x})的父亲不是( ext{y})
3:( ext{x})有右孩子。
然后你就可以写出( ext{lct})了。
上代码:
// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define il inline
const int maxn = 3e5 + 10;
using namespace std;
template<class T> il void rd(T& res) {
res = 0;char c;bool sign = 0;
for(c = getchar();!isdigit(c);c = getchar()) sign |= c == '-';
for(;isdigit(c);c = getchar()) res = (res << 1) + (res << 3) + (c ^ 48);
(sign) && (res = -res);
return;
}
int n,m,i,j,k,root;
int ch[maxn][2],size[maxn],fa[maxn],val[maxn],sum[maxn];
bool tag[maxn],dbg;
il bool isroot(int o) {
return ch[fa[o]][0] != o && ch[fa[o]][1] != o;
}
il void push_up(int o) {
size[o] = size[ch[o][0]] + size[ch[o][1]] + 1;
sum[o] = sum[ch[o][0]] ^ sum[ch[o][1]] ^ val[o];
return;
}
il void _swap(int& x,int& y) {
x ^= y ^= x ^= y;
return;
}
il void push_down(int o) {
if(tag[o]) {
_swap(ch[o][0],ch[o][1]);
tag[ch[o][0]] ^= 1;
tag[ch[o][1]] ^= 1;
tag[o] = 0;
}
return;
}
il int which(int o) {
return ch[fa[o]][1] == o;
}
il void rotate(int o) {
int f = fa[o],gf = fa[f],whi = which(o);
if(!isroot(f)) ch[gf][which(f)] = o;
ch[f][whi] = ch[o][whi ^ 1];
fa[ch[o][whi ^ 1]] = f;
ch[o][whi ^ 1] = f;
fa[f] = o;fa[o] = gf;
push_up(f);push_up(o);
return;
}
il void splay(int o) {
stack<int> sta;
while(!sta.empty()) sta.pop();sta.push(o);
for(int i = o;!isroot(i);i = fa[i]) sta.push(fa[i]);
while(!sta.empty()) push_down(sta.top()),sta.pop();
for(int f = fa[o];!isroot(o);rotate(o),f = fa[o]) {
if(!isroot(f)) rotate(which(f) == which(o) ? f : o);
// if(!dbg) printf("%d
",o);
}
push_up(o);
return;
}
il void access(int o) {
for(int y = 0;o;o = fa[y = o]) {
splay(o);
ch[o][1] = y;
push_up(o);
}
}
il int get_root(int o) {
access(o);splay(o);
while(ch[o][0]) push_down(o),o = ch[o][0];
return o;
}
il void make_root(int o) {
access(o);
splay(o);
tag[o] ^= 1;
return;
}
il void link(int x,int y) {
make_root(x);
fa[x] = y;
}
il void split(int x,int y) {
make_root(x);
access(y);
splay(y);
}
il void cut(int x,int y) {
split(x,y);
if(ch[y][which(x) ^ 1] || fa[x] != y || ch[x][1]) return;
fa[x] = ch[y][0] = 0;
push_up(y);
return;
}
int main() {
rd(n);rd(m);
for(int i = 1;i <= n;i++) rd(val[i]),sum[i] = val[i];
for(int i = 1,op,x,y;i <= m;i++) {
rd(op);rd(x);rd(y);
switch(op) {
case 0: {
split(x,y);
printf("%d
",sum[y]);
break;
}
case 1: {
if(get_root(x) != get_root(y)) link(x,y);
break;
}
case 2: {
if(get_root(x) == get_root(y)) cut(x,y);
break;
}
case 3: {
val[x] = y;
access(x);
splay(x);
push_up(x);
break;
}
}
}
return 0;
}