树链剖分是将树分成若干条链后处理,以达到更优秀复杂度的方法,长链剖分和重链剖分是两种常用的树链剖分的方式。
重链剖分
定义
重链剖分利用重儿子子树大小的性质,在处理路径/子树上的信息维护时,结合数据结构能做到单次 (Oleft(fleft( n ight) imes log n ight)) 的优秀复杂度,其中 (f left( n ight)) 表示单个重链操作的复杂度。同时可以优化 dp。
-
重儿子:所有儿子中子树大小最大的。
-
轻儿子:重儿子外的其他儿子。
-
重边:节点到其重儿子的边。
-
轻边:节点到其轻儿子的边。
-
重链:收尾相接的重边组。
一般来讲,重链也可以指极长的重链(不可向两头延伸)。
路径与子树信息维护
如果只维护子树上的信息,显然只需要预处理出 dfs 序,这样每一个点的子树都是 dfs 序上的一个连续区间,然后使用线段树等数据结构去维护。然而树上的一条路径不能保证在 dfs 序上呈一个连续区间,这样就需要我们对 dfs 序进行一些调整。
具体的做法是:对于一个节点,先 dfs 重儿子,再 dfs 轻儿子,这样可以保证一条重链上的所有节点一定是一段连续区间,因为是连续处理的。
以模板题举例。对于一条路径,我们可以把它剖分成 (Oleft(log n ight)) 条重链(或重链上的区间),在每条重链上可以用线段树进行修改与查询。
为什么是这样呢?观察重儿子的定义,会发现一个很好的性质:任意节点轻儿子的子树大小不超过其父亲的一半,因为它的重儿子一定比它多。对于任意两点 (u,v),记它们的 LCA 为 (w),可以把它们之间的路径分成两段:从 (u) 到 (w) 和 从 (u) 到 (w)。以从 (v) 到 (w) 举例,对于路径上的任意一条重链,记其上端点(深度最小的点)为 (i),则 (i) 一定是其父亲的轻儿子。所以,每经过一条重链,当前节点的子树大小至少会乘上 (2)。这样我们可以知道,任意一个点到根节点的路经上不会经过超过 (lceillog n ceil) 条轻边。所以处理时,记当前点为 (now),那么先 (Oleft(log n ight)) 修改 (now) 所在的重链,然后跳到它上方的一条重链继续处理,单次操作就是 (Oleft(log^2 n ight))。
此类改一改板子即可通过的习题非常多,这里只挂一道模板题。
dsu on tree
dsu on tree 的中文名字是树上启发式合并,由于太长所以一般不这么叫。树上启发式合并就是在树上做的启发式合并,由于太显然我想不到一个更好的解释。名字的话,我想应该是来源于 dsu 的按秩合并,道理一样,都是把小的暴力合并到大的上面去。
对于树上的问题,就是对于一个节点的信息,先 (Oleft( 1 ight)) 继承其重儿子,再暴力计算其轻儿子。注意这个思想在长链剖分会再次用到。
关于复杂度的证明,和上面的大同小异。由于任意一个点到根节点的路经上不会经过超过 (lceillog n ceil) 条轻边,所以对于一个点的暴力计算的贡献,只能是经过轻边时被暴力合并一次。所以总的时间复杂度是 (Oleft( fleft( n ight) imes n log n ight)),其中 (f left( n ight)) 表示单点合并的复杂度。
部分动态 dp
动态 dp 的形式一般类似于:给一个很裸的 dp,然后告诉你带修,要不然怎么能叫 动态dp
(逃。树上的这种问题,一般是用重剖或者 LCT 来维护树上路径间的(矩阵)乘积。且不论常数问题,虽然复杂度多一个 (log),但是重剖比 LCT 好写很多(线段树之于平衡树),在考场上效益较高。
一般过程是:先把 dp 改写成(广义)矩阵乘法的形式,然后就可以支持修改了,查询就是区间的矩阵求积,用线段树维护,可以理解为线段树套(广义)矩阵乘法。
长链剖分
定义
长链剖分在处理某些关于深度的信息的维护时可以达到线性的复杂度,可以在线 (Oleft( n log n ight)-Oleft(1 ight)) 求 (k) 次祖先。一般来讲,用处不如重剖多,但在特殊条件下不失为一种复杂度极为优秀的做法。为什么叫长剖呢,因为链很长。(雾
-
重儿子:所有儿子中子树深度最大的。
-
轻儿子:重儿子外的其他儿子。
-
重边:节点到其重儿子的边。
-
轻边:节点到其轻儿子的边。
-
重链:收尾相接的重边组。
为方便区分 (个人习惯),以下把重链称作长链。
性质
-
长链个数不超过 (n),且所有长链节点数目之和为 (n)。
-
子树深度 (≥k) 的点所在长链长度 (≥k)。
-
从一个点到根节点,轻边数目不超过 (Oleft( sqrt{n} ight)) 级别。
对于性质 (3),解释是:对于从给节点到跟的路经上的若干条长链,上面的长链的长度严格大于下面长链的长度,因为下面长链+两链之间的轻边就已经严格大于。所以最坏的情况是,轻重轻重重轻重重重轻重重重重......所以是从 (2) 开始的,每一项严格大于前一项的数列。而其和不超过 (n),所以,轻边数目不超过 (Oleft( sqrt{n} ight)) 级别。
树上 k 次祖先
长链剖分优化 dp