LCT小结
LCT:
类似于树链剖分吧我是这么认为的
要学会LCT首先你要先学会Splay及其区间翻转操作
基础
- 辅助树:
①Splay以深度为为关键字,维护每条链的信息(每条链一个splay)
②其根的父亲指向这条链的链顶的父亲
③记住每次splay时要先从上往下下放lazy
操作
-
判断是否是根
Isroot(x):只要判断这个点x在splay上的根是否有它这个儿子即可(第②点) -
把这个点在原树上到根的链变为重链
Access(x):x到原树上每次往上跳,splay(x),断掉它的右儿子,接上上次的点即可(第①点) -
把当前点变为原树的根
Makeroot(x):Access(x) splay(x) 区间翻转即可(第①点) -
找x在原树上的根
Findroot(x):Access(x) Splay(x) 一直跳左儿子即可(第①点) -
弄出x,y在原树上的路径(x,y连通)
Split(x, y):Makeroot(x) Access(y) Splay(y) 此时y的左儿子为x(splay时的性质) -
接原树边x,y(x,y不连通)
Link(x, y):Makeroot(x) x认y作splay中的fa即可 -
断原树边x,y(x,y连通)
Cut(x, y):Split(x, y) 此时y为x的父亲,直接x的父亲y的左儿子=0
一些套路
LCT可以做无排序的kruskal(problem:魔法森林)
边权:新建点变为点权
LCT可以动态维护连通性
等。。。
听说findroot不splay会被卡
我没加没被卡过(逃
最好加一下
板子
IL int Son(RG int x){
return ch[1][fa[x]] == x;
}
IL int Isroot(RG int x){
return ch[0][fa[x]] != x && ch[1][fa[x]] != x;
}
IL void Update(RG int x){
//维护信息
}
IL void Reverse(RG int x){
if(!x) return;
rev[x] ^= 1, swap(ch[0][x], ch[1][x]);
}
IL void Pushdown(RG int x){
if(!rev[x]) return;
rev[x] = 0, Reverse(ch[0][x]), Reverse(ch[1][x]);
}
IL void Rotate(RG int x){
RG int y = fa[x], z = fa[y], c = Son(x);
if(!Isroot(y)) ch[Son(y)][z] = x; fa[x] = z;
ch[c][y] = ch[!c][x], fa[ch[c][y]] = y;
ch[!c][x] = y, fa[y] = x, Update(y);
}
IL void Splay(RG int x){
S[S[0] = 1] = x;
for(RG int y = x; !Isroot(y); y = fa[y]) S[++S[0]] = fa[y];
while(S[0]) Pushdown(S[S[0]--]);
for(RG int y = fa[x]; !Isroot(x); Rotate(x), y = fa[x])
if(!Isroot(y)) Son(x) ^ Son(y) ? Rotate(x) : Rotate(y);
Update(x);
}
IL void Access(RG int x){
for(RG int y = 0; x; y = x, x = fa[x]) Splay(x), ch[1][x] = y, Update(x);
}
IL int Findroot(RG int x){
Splay(x); while(ch[0][x]) x = ch[0][x]; Splay(x); return x;
}
IL void Makeroot(RG int x){
Access(x), Splay(x), Reverse(x);
}
IL void Link(RG int x, RG int y){
Makeroot(x), fa[x] = y;
}
IL void Split(RG int x, RG int y){
Makeroot(x), Access(y), Splay(y);
}
IL void Cut(RG int x, RG int y){
Split(x, y), ch[0][y] = fa[x] = 0, Update(y);
}