形式陈述
分解不变量
在有根树 理路 有根树与祖先关系 Rooted tree · Ancestor relation in a rooted tree · Parent and depth in a tree 在树中选定根后,由唯一根路径定义父子、祖先、深度与子树。 中令 s i z e ( v ) 为子树大小;每个非叶节点选子树最大的一个孩子为重儿子,其余父子边为轻边。重边组成互不交的链,随后采用先访问重儿子、再访问轻儿子 的 DFS 前序编号,把每条重链放到连续数组区间,并记录链顶 head。任意 DFS 次序未必有重链连续性;这里固定重儿子优先,且相同子树大小时按固定编号打破平局。
若 ( v , u ) 是轻边,则 s i z e ( u ) ≤ s i z e ( v ) / 2 :否则 u 会比选中的重儿子更大,或两个孩子都大于一半而总大小超出 v 。因此任意根叶路径每经过一条轻边,剩余子树至少减半,轻边数至多 ⌊ log 2 n ⌋ 。
路径查询
查询 u –v 时,若两点链顶不同,把较深链顶一侧从 head 到节点的数组区间交给线段树 理路 线段树 Segment tree 把区间递归分解为规范节点,并在每个节点保存幺半群聚合值的平衡树结构。 ,再跳到 head 的父亲;最终两点同链,处理最后区间。共拆 O ( log ( n + 1 ) ) 段,若每段查询 O ( log ( n + 1 ) ) ,总计 O ( log 2 ( n + 1 ) ) 。子树节点在 DFS 序中天然连续,子树查询通常只需一个区间。
直觉
重边让路径尽量留在同一条连续链上;每次不得不跨轻边时,所在子树规模至少缩小一半。于是整条树路径虽然可能穿过很多节点,却只会被切成对数个数组区间,底层区间结构再负责聚合或更新这些片段。
图片加载失败 重链剖分示意图
例子与边界
图示的两段路径
图中根为 1 ,重链为 1 − 2 − 4 − u 与 3 − 7 − v ,另有叶子 5 接在 2 下、6 接在 3 下。重儿子优先的前序数组为
[ 1 , 2 , 4 , u , 5 , 3 , 7 , v , 6 ] . 因此 u 到 v 的点路径,由 [ 0 , 4 ) 的反向 序列 [ u , 4 , 2 , 1 ] 接上 [ 5 , 8 ) 的正向序列 [ 3 , 7 , v ] 组成。图中的两个区间并集说明哪些位置入选,并不规定非交换聚合的执行顺序。
同一编号还保留子树区间:2 的子树是 [ 1 , 5 ) ,3 的子树是 [ 5 , 9 ) 。链连续和子树连续能同时成立,靠的是这份具体的 DFS 访问顺序。
失效边界
HLD 固定树的拓扑和根。点权或边权变化不影响分解;link/cut 改变子树大小后,重儿子选择可能在很多位置改变,应使用适合动态森林的结构,或明确支付重建成本。重链总数可以很多,受控的是单条路径跨越的轻边数。
推论与应用
两遍构建的确切次序
第一次利用树的 DFS 遍历 理路 树的 Euler Tour 技巧 Euler tour technique for trees 用 DFS 次序把子树或树上行走线性化,并区分三种常见序列。 计算 parent、depth、size 和 heavy child。第二次递归编号:
text decompose(v, h):
head[v] = h
pos[v] = timer++
if heavy[v] exists:
decompose(heavy[v], h)
for each other child c of v:
decompose(c, c)
1 2 3 4 5 6 7
两遍各扫描每条树边常数次,时间为 O ( n ) 。第二遍仍是完整 DFS 前序,所以 v 的子树恰对应 [ p o s [ v ] , p o s [ v ] + s i z e [ v ] ) ;同时,在进入任何轻子树前已连续编号当前整条重链。不能把轻子树任意搬到整段遍历末尾,否则可能保留重链连续,却破坏子树区间。
有方向的路径聚合
设节点值属于幺半群 理路 幺半群 Monoid 具有双侧单位元的半群。 ( M , ∘ , e ) 。用线段树 理路 线段树 Segment tree 把区间递归分解为规范节点,并在每个节点保存幺半群聚合值的平衡树结构。 在每个区间保存正向与反向两个摘要;交换两方向不是对聚合值求逆。例如矩阵乘积的逆并不等于把原矩阵次序反过来的乘积。
查询从原始 u 走到原始 v 时,维护 L = R = e :L 是已经从 u 端收集的前缀,R 是通向 v 端的后缀。链头不同时,比较它们的深度:
若 u 侧链头较深,查询 [ p o s [ h e a d [ u ] ] , p o s [ u ] + 1 ) 的反向摘要 Q ,令 L ← L ∘ Q ,再把 u 跳到链头的父节点
否则查询 v 侧对应区间的正向摘要 Q ,令 R ← Q ∘ R ,再把 v 跳到链头父节点
会合到同一条链后,按当前 u 到 v 的方向取中间摘要 Q 0 ,返回 L ∘ Q 0 ∘ R 。结合律允许合并区间,正反摘要和两个累积器负责保持顺序;不需要交换律或逆元。求和与最大值会隐藏次序错误,字符串标签则能直接暴露它。
每次向上越过轻边,所在祖先子树至少翻倍,所以每侧最多跳 O ( log ( n + 1 ) ) 段。若摘要占常数字、合并为常数时间,每段线段树查询为 O ( log ( n + 1 ) ) ,总时间为 O ( log 2 ( n + 1 ) ) ;输出展开的长字符串另计长度成本。
点权、边权与区间更新
边 ( p a r e n t ( v ) , v ) 可存到较深端点 v 的数组位置。跨链时,链头位置代表向父节点跨出的那条轻边,仍须保留;最后同链区间才排除 LCA 的位置,以免多计 LCA 上方的入边。点权则保留 LCA。两端相同时,边路径为空,返回单位元;点路径含这一个顶点。
点修改只改一个位置;路径或子树更新则交给满足相应代数条件的 lazy 区间结构。这些操作不改变拓扑,原分解仍有效。静态数据配专门的链前缀摘要等结构时,可以省去每段查询的一层对数,但应单独说明它支持哪些聚合与更新。
参考资料
Daniel Sleator, Robert Tarjan, A Data Structure for Dynamic Trees , JCSS, 1983.
Robert Tarjan, Data Structures and Network Algorithms , SIAM, 1983, tree path decomposition.