LCT简要总结
实链剖分
简单来说就是把边分为实边和虚边,然后做不同的维护,不过是可以动态维护。
这样可以减小操作数,可以用势能分析证明时间复杂度,先欠着。
LCT 中分了实边和虚边,对实边构成的实链用 Splay 维护,然后通过虚边把 Splay 连起来,因为维护的是一条连续的链,所以满足中序遍历 Splay 得到的点按深度严格递增。
因为实链是一条链,所以每个节点最多和 2 个实点(祖先和儿子)相连,所以 Splay 中左儿子维护原树的祖先,右儿子维护儿子。
注意 LCT 在实链中是父子互认的,但虚边是只认父不认子的。
LCT的基本操作
-
nort
判断一个点是不是当前实链的顶端。
若 x 是当前实链的顶端,那它连向 fa 的边一定是虚边,且 fa 的儿子一定不会有 x。
反之则不是。
bool nort(int x){return lc(fa(x))==x||rc(fa(x))==x;}
-
rotate&&splay
和普通的 Splay 没啥区别,不过要 nort 特判一下,代码不放了。
-
access
这是 LCT 最核心的操作了。
打通并分离出从 x 到根节点的路径(全变为实边),以便于维护。
操作:
1.把 x 旋到它所在的 Splay 的根。
2.把 x 的右儿子设为上一轮处理的节点 y(把 x 的实边连向 y,原本的设为虚边)。
3.通过 \(fa_x\) 不断往上跳到父亲节点,直到根节点。
void access(int x){for(int y=0;x;x=fa(y=x)) splay(x),rc(x)=y,up(x);}
-
makert
把 x 设为 LCT 的根。
\(access(x)\) 打通一下。
这时 x 一定是最右边的点,即中序遍历的最后一个点(深度最深)。
所以把 x 旋到根节点后,做一次翻转操作,x 就变成中序遍历的第一个点即根节点了。
void makert(int x){access(x),splay(x),rev(x)^=1;}
-
findrt
找到 x 所在原树的根,用来判断联通性。
先 \(access(x)\) 打通并 \(Splay(x)\) 一下,这时 x 和 根节点在同一个 Splay 上。
而根节点一定是中序遍历的第一个点(最左的点),所以从 x 不断往左儿子走,走到尽头就是根节点。
int findrt(int x){
access(x),splay(x);
while(lc(x)) down(x),x=lc(x);
return splay(x),x;
}
-
link
把 x 与 y 连一条边。
把 x 设为根节点,然后判一下 x 与 y 联不联通,不联通就把 \(fa_x\) 设为 y(连一条虚边)。
void link(int x,int y){makert(x);if(findrt(y)!=x) fa(x)=y;}
-
cut
删除 x 与 y 的边。
把 x 设为根节点。
这时若 y 与 x 有边相连,则有:
-
y 所在的根为 x(联通)。
-
y 的 fa 为 x(有边相连)。
-
y 没有左儿子,否则 \(dep_x<dep_{lc_y}<dep_y\),就不相连。
然后直接断掉即可。
void cut(int x,int y){makert(x);if(findrt(y)==x&&fa(y)==x&&!lc(y)) fa(y)=rc(x)=0;up(x);}
code
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,r,l) for(int i=(r);i>=(l);--i)
const int N=2e5+5;
class LCT{
private:
struct tree{int c[2],v,f,s;bool r;}t[N<<2];
#define ch(x,op) t[x].c[op]
#define val(x) t[x].v
#define sum(x) t[x].s
#define rev(x) t[x].r
#define fa(x) t[x].f
bool nort(int x){return ch(fa(x),0)==x||ch(fa(x),1)==x;}
void up(int x){sum(x)=val(x)^sum(ch(x,0))^sum(ch(x,1));}
void down(int x){if(rev(x)) swap(ch(x,0),ch(x,1)),rev(ch(x,0))^=1,rev(ch(x,1))^=1,rev(x)=0;}
void push(int x){if(nort(x)) push(fa(x));down(x);}
void rotate(int x){
int y=fa(x),z=fa(y),k=(ch(y,1)==x),w=ch(x,k^1);
if(nort(y))ch(z,ch(z,1)==y)=x;
ch(y,k)=w,ch(x,k^1)=y;
if(w)fa(w)=y;fa(x)=z,fa(y)=x;
up(y),up(x);
}
void splay(int x){
push(x);
while(nort(x)){
int y=fa(x),z=fa(y);
if(nort(y)) rotate(ch(z,1)==y^ch(y,1)==x?x:y);
rotate(x);
}
}
void access(int x){for(int y=0;x;x=fa(y=x)) splay(x),ch(x,1)=y,up(x);}
void makert(int x){access(x),splay(x),rev(x)^=1;}
int findrt(int x){
access(x),splay(x);
while(ch(x,0)) down(x),x=ch(x,0);
return splay(x),x;
}
void split(int x,int y){makert(x),access(y),splay(y);}
public:
void init(int l,int r){rep(i,l,r)cin>>val(i);}
void link(int x,int y){makert(x);if(findrt(y)!=x)fa(x)=y;}
void cut(int x,int y){makert(x);if(findrt(y)==x&&fa(y)==x&&!ch(y,0))fa(y)=ch(x,1)=0;up(x);}
void change(int x,int val){splay(x),val(x)=val,up(x);}
int query(int x,int y){return split(x,y),sum(y);}
#undef ch
#undef val
#undef sum
#undef rev
#undef fa
}LCT;
int n,m,op,x,y;
void solve(){
cin>>n>>m;
LCT.init(1,n);
while(m--){
cin>>op>>x>>y;
switch(op){
case 0:cout<<LCT.query(x,y)<<'\n';break;
case 1:LCT.link(x,y);break;
case 2:LCT.cut(x,y);break;
case 3:LCT.change(x,y);break;
}
}
}
int main(){solve();}

浙公网安备 33010602011771号