长链剖分学习笔记

关于长链剖分

长链剖分,也属于树链剖分的一种方式,但是其与经典的重链剖分又不太一样。
在重链剖分中,我们评判儿子重或轻的方式是比较其子树节点数量
在长链剖分中,我们以子树中最深的叶节点深度的大小来比较。
其他步骤与重链剖分类似,都是两遍DFS即可解决,都是O(n)的复杂度。


接下来,我们来看两个长链剖分的经典问题.

1.询问某个点的k级祖先

时间复杂度要求: O(nlogn).

需要技能:

  • 长链剖分
  • 倍增

为什么要长链剖分:

我们利用的性质是:任意一个点的k级祖先所在链的链长一定大于等于k。

证明:

1.如果这个点和他的k级祖先在一条链上,那么结论显然成立。

2.如果不在一条链上,那么一开始这个祖先没有选择这棵子树是因为别的子树更深,故而所在链一定是大于k的,结论仍然成立。


操作步骤

  • 预处理
  1. 对树进行长链剖分,记录每个点的链头和深度
  2. 倍增预处理出每个点2^n级祖先
  3. 如果某条链的长度是len,那么在链头处记录链头向上的len个祖先,并记录向下的len个链的元素
  4. 记录每个数最高位1是多少
    总时间复杂度: O(nlogn)
  • 查询
  1. 先利用倍增数组跳K的最高位的1层,设剩余层数为k,可以发现

  2. 利用之前证明的性质,我们可以发现当前节点所在链链长一定严格大于k。如果链头在当前节点的k级祖先上面,那么我们利用链头向下的数组可以O(1)得到答案,否则利用向上的数组同样可以O(1) 得到答案。


2.统计每个点子树中以深度为下标的可合并信息

这个应用的思想十分类似Dsu on tree
思想是当前结点直接继承重儿子的信息,轻儿子信息暴力统计。
因为长链剖分的特殊性,因此这个应用仅限于以深度为下标。
更一般的就只能使用Dsu on tree做到O(nlogn)的复杂度了。

更具体地:
若需求出点x的信息,首先从重儿子继承。
因为以深度为下标,因此只需要对数组进行移位即可。
后面我们会提到移位的实现方法。

接着,轻儿子的信息暴力合并到当前的信息中。

时间复杂度O(n)。
分析如下:
每个点x只会暴力统计其所有轻儿子的信息,而每个轻儿子的信息大小为该轻儿子所在长链长度。
而当递归到x的父节点fa(x)时,若xx不是fa(x)的重儿子,则fa(x)会暴力统计大小为x长链长度的信息。
故,每个长链只会对转移的复杂度做一次大小为其长度的贡献。
因此根据性质1,总时间复杂度为O(n)

移位的实现方法
本处仅提到一种实现方式,但方法不只有这一种。
用指针维护动规数组,在重儿子传递给父亲时将指针左移/右移一位。(左移还是右移根据转移方程决定)
而给其他轻儿子分配不相交的内存即可。

不难发现,这样实现的空间复杂度仍为O(n)。
特别注意,这样实现也有相应的缺点,即若动规的数组高于一维,那么数组是一次性的,不能在完成转移后查询中间过程的值。
原因是部分内存被共用掉了。

小结

长链剖分在维护一些与深度有关的信息时十分强力有效。
在考虑题目时灵活运用其性质往往可以得到较好的结果。


例题:

猜你喜欢

转载自www.cnblogs.com/Kv-Stalin/p/9246578.html
今日推荐