LCT 详解

本文详细介绍了Link-Cut Tree(LCT)这一高级数据结构,它结合了Splay Tree的特点来维护树剖。LCT通过access、evert、findrt、link和cut等操作实现树的动态变化。文章通过实例解释了LCT如何维护子树信息,并提供了基础和复杂情况下的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天学了个叫 “LCT” 的东西,很多人估计越听越懵。

Link-Cut Tree 确实是个很不好理解的东西,就连我也是。

就算是老师讲也感觉很模糊。


首先,这个 LCT 也可以理解为“用 Splay 来维护树剖”,这里的树剖指的是实链剖分(专门为 LCT 服务的剖分方式)。

一棵树中,处于同一条实链的节点用同一个 Splay 维护。由于 Splay 灵活多变,完全可以代替线段树。

然后,每一个 Splay 内维护的节点键值是他们在原来树中的深度。即对于一个 Splay 节点键值 x x x,左子树的所有节点在树中的深度都小于 x x x,右子树的所有节点在树中的深度都大于 x x x

于是,代码的前半部分完全用来写 Splay。

注意,有一个重点就是,树中的实链(即一棵 Splay)中的节点连的是双向边,而非实边连的是单向边(儿子认父,父不认儿子),这样有助于判断一个节点是否是 Splay 的根。

但是,之后呢?我们定义一个函数 a c c e s s ( x ) access(x) access(x) 表示,现在把 r o o t , . . . , x root,...,x root,...,x 这一条链搞成一条实链,并且 x x x 下面不接任何实边(注意 r o o t , . . . , x root,...,x root,...,x 这些节点原本接的实边要删除)

如上图,红色的为实边(有些节点也可以不接实边,比如 7 7 7)。

接下来执行 a c c e s s ( 5 ) access(5) access(5)

执行 a c c e s s ( 8 ) access(8) access(8)

可以发现,每做一次 a c c e s s ( x ) access(x) access(x),其实就是把 r o o t , . . . , x root,...,x root,...,x 重构成一条实链,并单独用一个 Splay。

a c c e s s access access 函数是 LCT 中最重要的部分,也是 LCT 中唯一连接实线的方式。

void access(int x) //x 不为空,直到根为止
{
   
	int y=0;
	while(x)
	{
   
		splay(x);
		a[x].c[1]=y; //节点 x 原来接的实线清空,重新接到下面的节点,原来接的节点必然深度更深,即在平衡树右边
		a[y].fa=x;
		pushup(x);
		y=x;
		x=a[x].fa; //下一次更新实线用到
	}
}

函数 e v e r t ( x ) evert(x) evert(x) :把 x x x 作为 x x x 所在树的与根。

考虑用 a c c e s s ( x ) access(x) access(x) x x x r o o t root root 连接,然后伸展到平衡树的根,最后利用翻转来保证所在平衡树的深度。

比如执行 e v e r t ( 8 ) evert(8) evert(8),连接了 1 , 7 , 8 1,7,8 1,7,8,以深度为键值建出平衡树:

8 8 8 节点伸展到根:

你会发现,如果把 8 8 8 作为根,有且只有这棵平衡树的深度需要重构,只需要对整棵树进行翻转即可。

翻转必然要打标记,必然要下放标记。因此在每次 Splay 伸展时,必须先从根传标记。

void evert(int x)
{
   
	access(x);
	splay(x);
	a[x].rev^=1; //翻转标记
}

函数 f i n d r t ( x ) findrt(x) findrt(x) 查找 x x x 所在的树的根。

显然,先用 a c c e s s access access 连接 r o o t root root x x x,然后在平衡树中找深度最小的,即一直往左走。

int findrt(int x)
{
   
	access(x); //连接 root,x
	splay(x); //把 x 伸展到平衡树的根,也可以理解为找平衡树的根的一种方式
	while(a[x].c[0]) //一直往左走
	{
   
		x=a[x].c[0];
	}
	return x;
}

函数 l i n k ( x , y ) link(x,y) link(x,y),连接原来树上 ( x , y ) (x,y) (x,y) 两个节点。

类似于并查集的合并,我们可以先把 x x x 作为树根

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值