03_树链剖分 --- 长链剖分

Hello,大家好,我们又见面了,今天,我来继续给大家介绍算法竞赛进阶内容。上一期我给大家分享了重链剖分,这一期,我来给大家分享树链剖分的另外一种常见形式 --- 长链剖分

相信这篇博客一定会对大家有所帮助~~~

想要平滑的学习这一期内容,还是希望大家首先去看一下本专辑的上一篇文章《重链剖分》。

那么废话不多说,我们直接开始。

一:引入

长链剖分 vs 重链剖分

重链剖分:根据子树的大小,把树拆分成若干条互不相交的重链;

长链剖分:根据子树的深度(高度),把树拆分成若干条互不相交的长链

二:相关概念

以下是树的长链剖分相关的概念:

  1. 长儿子:父节点的所有儿子中,子树深度最大的结点

  2. 短儿子:父结点中,除了长儿子以外的儿子。

3.长边父结点与长儿子相连的边

  1. 短边:父结点与短儿子相连的边。

  2. 长链:由多条长边连接而成的路径。特殊的,如果短儿子是叶子节点时,单独构成一条长链。

下面通过一张图来理解一下这些概念:

三:长链剖分

将一整棵树分成若干条长链的过程,就叫做长链剖分。

如下图所示:

上图就是将一棵树进行长链剖分之后的结果,大家是不是觉点有一点熟悉???没错,上一篇文章中的重链剖分也是这棵树,而且剖分结果是一模一样的。

但是,这并不表明重链剖分和长链剖分的结果是一样的,只是这棵树比较特殊,一般情况下,两者最终的剖分结果其实是不同的。大家可以多画几棵树,就可以发现两者其实是不同的。

四:代码实现

1. 预备工作:

要实现长链剖分,先定义几个数组:

  1. fax 表示:结点 x 的父亲节点;

  2. depx 表示:结点 x 的深度;

3. lenx 表示:结点 x 走到叶子节点,最多经过的点数;

4. sonx 表示:结点 x 的长儿子;

5. topx 表示:结点 x 所在长链的顶部节点;

  1. dfnx 表示:结点 x 的 dfn 序;

具体代码实现:

两次 dfs 就可以维护出上面所列的信息

2. 第一次 dfs:

第一次 dfs 先维护前四个数组的信息:

cpp 复制代码
int n;
vector<int> edges[N];
int fa[N], dep[N], len[N], son[N], top[N], dfn[N], idx;

void dfs1(int x, int f)
{
	fa[x] = f; dep[x] = dep[f] + 1;
	for(auto y : edges[x])
	{
		if(y == f) continue;
		dfs1(y, x);
		if(len[y] > len[son[x]]) son[x] = y; 
	}
	len[x] = len[son[x]] + 1;
} 

2. 第二次 dfs:

第二次 dfs 维护最后两个数组的信息:

cpp 复制代码
void dfs2(int x, int t)
{
	top[x] = t; dfn[x] = ++idx;
	if(son[x]) dfs2(son[x], t);
	for(auto y : edges[x])
	{
		if(y == fa[x] || y == son[x]) continue;
		dfs2(y, y);
	}
} 

五:性质

长链剖分有以下两个性质:

性质一:

任何一个结点 x 的 k 级祖先 y 所在长链的长度一定大于等于 k。

这个结论显然正确,可以通过反证法证明。

1. 如果 y 所在的长链长度小于 k,那么它就不是长链了,显然从 y 走向 x 这条链更优。

2. 于是 y 所在长链的长度一定大于等于 k,性质是成立的。

性质二:

一个结点跳跃长链(沿着长链跳)到根节点,跳跃的次数最多为 O(根号 n)。

证明:

1. 如果一个节点 x 从一条长链跳到了另外一条长链上,那么跳跃到的这一条长链的长度不会小于之前的长链长度。(由性质一得到)

2. 考虑最差的情况,每一次跳跃的链长分别为 1,2,3,4,......k,一共跳 n 个结点,由 n = 1 + 2 + 3 + 4 + ...... +k,可知最多跳根号 n 次。

六:总结

因为长链剖分向上跳的次数比较多,时间复杂度较高,根号 n 级别,我们一般不会使用长链剖分来解决上一篇文章中的路径修改和查询问题。使用长链剖分解的话,不具有优势

但是,长链剖分有它独特的用武之地,长链剖分和重链剖分是相辅相成的。

我们一般用长链剖分后的特征,来处理一些特定的问题以及优化 dp。

之后,我会给大家介绍长链剖分的应用场景。这一期就先到这里了。

如果大家感觉有所收获的话,别忘了一键三连,我们下一期再见!!!

相关推荐
凡人叶枫5 分钟前
Effective C++ 条款24:若所有参数皆须要类型转换,请为此采用 non-member 函数
linux·前端·c++·算法·嵌入式开发
洛水水6 分钟前
【力扣100题】87.只出现一次的数字
数据结构·算法·leetcode
HZ·湘怡7 分钟前
排序算法之希尔排序(2)--菜鸟先飞
数据结构·算法·排序算法·希尔排序
乐观勇敢坚强的老彭9 分钟前
2026全国青少年信息素养大赛(Python小学组)复赛复习讲义
python·算法·数学建模
林间码客17 分钟前
02数据挖掘:数据属性、类型与相似性度量
人工智能·算法·机器学习
阿标在干嘛19 分钟前
从“拍脑袋”到“数据驱动”:政策平台的A/B测试实践
大数据·人工智能·算法·ab测试
实在智能RPA23 分钟前
气象预警Agent等级判定算法:2026年AI驱动的概率集合预报与自动化闭环实践
人工智能·算法·ai·自动化
风筝在晴天搁浅1 小时前
LeetCode CodeTop 82.删除排序链表中的重复元素Ⅱ
算法·leetcode·链表
189228048611 小时前
NV114固态MT29F16T08EWLEHD6-MES:E
人工智能·算法·缓存·性能优化
Tairitsu_H1 小时前
[LC优选算法#4] 滑动窗口 | 串联所有单词的⼦串 | 最⼩覆盖⼦串
c++·算法·滑动窗口