LCT 学习笔记
简述
可能叫动态链剖分会比较合适?
回顾重链剖分,我们将 x 所有儿子中子树 siz 最大的那个儿子钦定为重儿子,一个结点与它的重儿子的连边称为重边,其余称为轻边。重边和轻边形成的链把整棵树剖分成了若干个 dfs 序上的连续段,这样我们就能使用诸如线段树等静态数据结构维护链上的信息了。
重剖的局限性在于它所维护的树的形态必须是静态的,在面对一些需要动态更新原树形态的问题难免乏力。于是就有了 LCT(动态树),它支持:维护一个森林,更换某棵树的根(makeroot),新建边(link),删除边(cut),修改及查询链上的信息等操作。
LCT
把边分为实边和虚边,每个结点最多有一个实儿子(可以没有实儿子)。实边连成的链称为实链,改为以每个结点的深度为关键戳用 Splay 维护实链上的信息,这样更加灵活。实链与实链之间通过虚边相连,这里采用“认父不认子”的处理方法把实链对应的 Splay 连接起来。即:将每棵 Splay 的 root 的父亲结点设为它所维护实链的链顶在原树意义上的父亲 \(x\),但是并不更改 \(x\) 在 Splay 里的左儿子和右儿子。这样的好处在于:既保留了 Splay 本身的功能性,又能从某个结点出发还原出它到根结点的路径,从而还原原树结构。同时,Splay 可以方便地提取/分离子树,这又使得我们能够方便地切换虚实边,从而断掉一条实链,以及自底向上连接若干条实链。这些优势将在 access 操作里体现。
access
access 操作是 LCT 的核心操作,它的效果是把 \(x\) 在原树意义上到根结点的路径中的所有边都变为实边,相应地,不在路径上且连接了路径上结点的所有实边要变成虚边。它的流程是:先新建一个空结点 \(y\),把 \(x\) splay 到根结点,将其右儿子改为 \(y\)(\(x\) 成为根结点后所有深度大于 \(x\) 的结点便都集中在了 \(x\) 的右子树里,更改右儿子相当于把 \(x\) 与其实儿子之间的实边切换为虚边,也就是断掉了 \(x\) 所在实链中更靠下的部分,并把 \(y\) 对应的实链接了上去 );然后令 \(y=x,x=fa_x\)(这里相当于跳到更上面的实链,继续把之前拼成的实链与上面的链拼接),重复第一步直到走到原树意义上的根结点。代码如下:
void access(int x){
int y=0,t=x;
//access 操作每次跳实链会把 top(y)->x 这条虚边变成实边,再把原来的 suf(x)->x 这条实边变成虚边
while(x){
splay(x);
ch[x][1]=y;fa[y]=x;//同时切断 x 所在实链中更深的那一截(但保留虚父亲),并把新的实链接上
push_up(x);
y=x;x=fa[x];
}
splay(t);
}
makeroot
在 access 操作的基础上,考虑把某棵树的根变为 \(x\) 会发生什么。设原树根为 \(r\),换根只会使原树上 \(x\) 到 \(r\) 的简单路径上的点的深度序列翻转。由于 Splay 是以深度为关键戳维护的,所以只需要先 access(x),然后把 \(x\) 所在 splay 整体翻转。这个也是经典操作,只需要额外维护一个懒标记。
void makeroot(int x){
access(x);
tag[x]^=1,swap(ch[x][0],ch[x][1]);
}
link
先把 \(x\) 所在树的根换成 \(x\),然后连一条 \(x\to y\) 的虚边。
int findroot(int x){
access(x);
while(ch[x][0]) x=ch[x][0];
return x;
}
void link(int x,int y){
if(findroot(x)==findroot(y)) return;
makeroot(x);
fa[x]=y;
}
cut
先判掉 \(x,y\) 没有直接连边的情况。
然后把 \(x,y\) 放在同一棵 Splay 里并让它们相邻,这个可以通过 makeroot 和 splay 操作实现
void cut(int x,int y){
makeroot(x);
access(y);
if(x==ch[y][0]&&!ch[x][1]) ch[y][0]=0,fa[x]=0,push_up(y);
}
其余操作
其余操作大概都是修改和维护链上信息,可以参考 Splay 的各种操作,一般来讲 Splay 能维护的 LCT 也能维护,如 https://www.luogu.com.cn/problem/P1501 [国家集训队]Tree II。
经典例题
- 弹飞绵羊。建一个虚拟结点表示被弹飞,问题转为 Link,Cut,维护树上两点路径长度,维护一个 dep 就能做了。

浙公网安备 33010602011771号