【学习笔记】动态树 Link-Cut Tree
1|0- 闲话
LCT 优秀博客:
- FlashHu 大佬的 cnblogs:https://www.cnblogs.com/flashhu/p/8324551.html
2|0- 动态树 Link-Cut Tree
2|1- 前置知识
- 「必学」Splay。
- 「重要」树链剖分 / 重链剖分(虽然并不需要用到,但是了解重链剖分的思想还是有用的)。
- 「必学」实链剖分。
实链剖分是一种动态的剖分方式。
对于一个点连向它儿子的所有边,我们选择一条边进行剖分。
被选择的边称为实边,其他边为虚边。
实边连接的儿子称为实儿子。
对于一条实边连接成的链,称为实链。
实链剖分的剖分结果是可变的,可以灵活调整。
和重链剖分的区别就是,重链剖分需要找到儿子子树大小最大的一个连重边,而实链剖分不需要。
2|2- 何为 Link-Cut Tree
给定一棵树,有以下操作:
- 修改
的点权。 - 求出
的简单路径的点权和。 - 修改
子树每一点的点权。 - 求出
子树的点权和。
一个很简单的「树链剖分」题目,不是吗?
如果我们增加几个操作呢?
- 断开
这一条边。 - 连上
这一条边。 - 把这棵树改成以
为根。
很显然,因为这棵树要动态删边和加边,且有换根操作,维护静态树的树链剖分就无法处理这类题目了,「动态树 Link-Cut Tree,LCT」应运而生。
具体的,LCT 可以维护以下操作(引自 FlashHu cnblogs):
- 查询、修改链上的信息(最值,总和等)
- 随意指定原树的根(即换根)
- 动态连边、删边
- 合并两棵树、分离一棵树
- 动态维护连通性
- 更多意想不到的操作
因为 LCT 是动态的数据结构,所以线段树等已不适合维护,引入「Splay」这种平衡树来维护之
LCT 实质上维护了一个森林,每棵树 都由若干棵 Splay 维护。有如下性质:
-
每棵 Splay 都维护一条原树的路径,这条路径满足节点深度依次增大,且中序遍历 Splay 得到的每个点的深度序列严格递增。单独的一个点也可以是一棵 Splay。
- 举个例子,这棵树的构造为
1(2,3)
,即 号节点为树根,深度为 , 号节点分别为它的左右儿子,深度为 。那么这个 Splay 森林可以是这样的:
,第一棵 Splay 维护 这条路径,深度单调递增( ),第二棵维护 ,单独的一个点。 ,第一棵 Splay 维护 这条路径,深度单调递增( ),第二棵维护 ,单独的一个点。 ,三个点都为一棵独立的 Splay。
注意
这棵 Splay 是不合法的,因为 的深度相等。 - 举个例子,这棵树的构造为
-
每个节点被包含且仅被包含在一棵Splay 内。
-
由以上两条性质我们可以得出,每个节点只能和它的儿子连一条实边,其余的儿子都和他连虚边,并且每一条虚边的儿子所在的 Splay 要指向这个节点。但是这个节点并不能指向其儿子的 Splay(即 FlashHu 博客中的认父不认子)。
3|0- 具体操作
3|1- access(x)
-
LCT 最核心的操作。
-
打通根节点和指定节点的路径,即把根节点和
中间的路径都变成 实边,形成一条以根节点开始,指定节点结束的 Splay。
来几张图
假设一开始实边和虚边是这么划分的:
那么所形成的 Splay 森林可能是这样的(绿框中为一个 Splay):
现在我们要
根据性质 3,原来有些实边要变虚(因为
我们一步一步自底向上拉。
首先
因为要 以指定节点结束,所以比他深且在一颗 Splay 中的点要去除。
因为性质 1,中序遍历 Splay 得到的每个点的深度序列严格递增,所以我们把
如下图:
接下来要打通
然后就可以连接
于是
代码很简单,只需要四步:
当前节点,转到根。- 找到它所指的父亲,换右儿子。
- 更新信息,
pushup
。 - 把当前节点变成它的轻边所指的父亲,转
。
3|2- makeroot(x)
- 把
拉到整棵树的根。
在介绍
- pushr(x)
我们注意到,将
引用几张图
这是一棵树,那么如果我们想翻转
但在这个地方我们可以考虑打个标记,标记的存在就只在于记录现在对于当前节点应不应该翻转两个子树。
接下来回到
首先显然要把根节点到
所以我们先
3|3- findroot(x)
- 找到
所在原树的根,主要用来判断两点的连通性(即如果 表明 在一棵树中)。
我们先把根节点到
注意往下找左儿子的时候,一定要下放翻转标记,不然可能会导致 Splay 信息不正确。
3|4- split(x,y)
- 指定出一条
路径的 Splay。
新的
先
3|5- isroot(x)
- 判断当前节点是否是它所在 Splay 的根。
原理很简单,如果他是 Splay 的根(即它和它的父亲连的是虚边),它的父亲的儿子里没有它(它的父亲连到它的实儿子了)。
3|6- link(x,y)
- 连上
的边。
可以自行决定把
代码也很简单,如下:
如果题目保证连边合法,代码就可以更简单:
如果
4|0- Reference
makeroot
等操作需要翻转一棵树,使得 Treap 等平衡树均已不适用,但是 FHQ Treap 或许也可以维护,详见 https://immortalco.blog.uoj.ac/blog/2342。
__EOF__

本文链接:https://www.cnblogs.com/TheSky233/p/17034263.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!