Link-Cut-Tree学习笔记
原理:将树分成一条条链,之后用Splay维护
一.定义数组
int son[MAXN][2], fa[MAXN], sum[MAXN], val[MAXN], sta[MAXN], lazy[MAXN];
son记录每个节点的两个重儿子
fa记录每个节点的父亲
(fa和son双向为重边,单向为轻边)
sum为本题特有的数组,含义为子树异或和,val为节点权值,lazy为翻转标记
二.基础操作
1.merge
void merge(int x){
sum[x] = sum[son[x][0]] ^ sum[son[x][1]] ^ val[x];
}
每当节点更新时要用一遍merge
2.spread
void spread(int x){
if(lazy[x]){
lazy[son[x][0]] ^= 1;
lazy[son[x][1]] ^= 1;
lazy[x] ^= 1, swap(son[x][0], son[x][1]);
}
}
传标记的函数
3.isroot
bool isroot(int x){
return son[fa[x]][0] != x && son[fa[x]][1] != x;
}
判断一个点是否为当前Splay块里的根
4.rotate
void rotate(int x){
int y = fa[x], z = fa[y], id = son[y][1] == x;
if(!isroot(y)) son[z][son[z][1]==y] = x;//和普通Splay不一样的地方,因为Splay的目的是旋转到当前Splay块的顶部
fa[son[x][id^1]] = y, son[y][id] = son[x][id^1];
fa[y] = x, son[x][id^1] = y, fa[x] = z;
merge(y), merge(x);//注意不要先merge(x)了
}
5.splay
void splay(int x){
//因为要保证lazy的传递,所以需要提前将这一条链上的点全部spread
sta[0] = 0, sta[++sta[0]] = x;
for(int i=x; !isroot(i); i=fa[i]) sta[++sta[0]] = fa[i];
for(int i=sta[0]; i>=1; i--) spread(sta[i]);
while(!isroot(x)){
int y = fa[x], z = fa[y];
if(!isroot(y)) (son[z][0] == y) ^ (son[y][0] == x) ? rotate(x) : rotate(y);
rotate(x);
}
}
三.普通操作
1.access
void access(int x){
for(int y=0; x; y=x, x=fa[x])
splay(x), son[x][1] = y, merge(x);
}
打通原树根到x的唯一路径(不多也不少)
2.find
int find(int x){
access(x), splay(x);
while(son[x][0]) x = son[x][0];
return x;
}
找这个树的根(即打通路径后的Splay块中深度最小的节点)
3.makeroot
void makeroot(int x){
access(x), splay(x), lazy[x] ^= 1;
}
换根操作,当splay(x)后,x为深度最大的点,所以无右节点,此时反转序列之后将变成深度最小的点。即新的数根。因为换根操作不会影响其他的Splay块的深度关系,所以x就是新的树根。
4.split
void split(int x,int y){
makeroot(x), access(y), splay(y);
}
打通一条(x o y)的路径,并且打完之后顶部Splay块以y为根
5.link
void link(int x,int y){
if(find(x) == find(y)) return;
makeroot(x), fa[x] = y;
}
在(x o y)之间连一条边,因为我们指定它连轻边,所以不用更新y的sum值
这里第3行不能写为
access(x), splay(x), fa[x] = y;
这等价于x所在Splay的根和y连边,而不是x
6.cut
void cut(int x,int y){
split(x, y);
if(son[y][0] == x && son[x][1] == 0) son[y][0] = fa[x] = 0;
merge(y);
}
第3行保证了x和y连接(即x和y之间不存在其他深度的点)(注意要(son[x][1]==0)不然有可能x的右儿子还会有节点)
Luogu模板代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline long long read(){
long long ans = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
f *= (ch == '-') ? -1 : 1, ch = getchar();
do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar();
while(ch >= '0' && ch <= '9');
return ans * f;
}
const int MAXN = 3e5 + 5;
struct LinkCutTree{
int son[MAXN][2], fa[MAXN], sum[MAXN], val[MAXN], sta[MAXN], lazy[MAXN];
void merge(int x){
sum[x] = sum[son[x][0]] ^ sum[son[x][1]] ^ val[x];
}
void spread(int x){
if(lazy[x]) lazy[son[x][0]] ^= 1, lazy[son[x][1]] ^= 1, lazy[x] ^= 1, swap(son[x][0], son[x][1]);
}
bool isroot(int x){
return son[fa[x]][0] != x && son[fa[x]][1] != x;
}
void rotate(int x){
int y = fa[x], z = fa[y], id = son[y][1] == x;
if(!isroot(y)) son[z][son[z][1]==y] = x;
fa[son[x][id^1]] = y, son[y][id] = son[x][id^1];
fa[y] = x, son[x][id^1] = y, fa[x] = z;
merge(y), merge(x);
}
void splay(int x){
sta[0] = 0, sta[++sta[0]] = x;
for(int i=x; !isroot(i); i=fa[i]) sta[++sta[0]] = fa[i];
for(int i=sta[0]; i>=1; i--) spread(sta[i]);
while(!isroot(x)){
int y = fa[x], z = fa[y];
if(!isroot(y)) (son[z][0] == y) ^ (son[y][0] == x) ? rotate(x) : rotate(y);
rotate(x);
}
}
void access(int x){
for(int y=0; x; y=x, x=fa[x]) splay(x), son[x][1] = y, merge(x);
}
void makeroot(int x){
access(x), splay(x), lazy[x] ^= 1;
}
int find(int x){
access(x), splay(x);
while(son[x][0]) x = son[x][0];
return x;
}
void split(int x,int y){
makeroot(x), access(y), splay(y);
}
void cut(int x,int y){
split(x, y);
if(son[y][0] == x && son[x][1] == 0) son[y][0] = fa[x] = 0;
merge(y);
}
void link(int x,int y){
if(find(x) == find(y)) return;
makeroot(x), fa[x] = y;
}
}LCT;
int main(){
int n = read(), m = read();
for(int i=1; i<=n; i++) LCT.val[i] = LCT.sum[i] = read();
while(m--){
int op = read(), x = read(), y = read();
if(op == 0) LCT.split(x, y), printf("%d
", LCT.sum[y]);
else if(op == 1) LCT.link(x, y);
else if(op == 2) LCT.cut(x, y);
else if(op == 3) LCT.access(x), LCT.splay(x), LCT.val[x] = y, LCT.merge(x);
}
return 0;
}