前言
轻重链剖分,常被称为树链剖分,是一种常用的维护树上信息的算法。 它以子树大小为依据,将节点划分为重儿子与轻儿子,从而使整棵树被剖分成若干条重链。 每个轻儿子都是一条重链的开始。一个节点只在一条重链上。 从树上任意一点到根节点,最多经过 $\log n$ 条连续的链。 利用这样的特殊性质,可以解决许多问题。 由于是练习笔记,本文不再赘述概念有关内容。
练习题
CF383C Propagating tree
给一棵树,点有初始权值。现在有修改与查询操作。 修改操作使 $x$ 的权值增加 $val$,使 $x$ 的儿子的权值减少 $val$,使 $x$ 的儿子的儿子的权值增加 $val \ldots$
由于只操作子树,并不需要剖分性质,可以直接维护 dfs 序与子树大小。 修改操作相当于一次区间操作,思考如何实现增减的变化。 可以发现,正负与深度的奇偶有关。 维护一个数据结构,定义下标为奇数的位置的值为该点的额外权值,为偶数的位置的值为该点的额外权值的相反数,那么就可以用一次区间加来实现了。 初始权值可以直接单独记录下来,也可以初始化的时候插入数据结构中。
|
|
[BJOI2018]求和
一棵树,问一条路径上点的深度的 $k$ 次方和。 $k \le 50$,一个很暴力的想法就是维护 $50$ 棵线段树进行大力树剖。 然而空间有些不够,考虑深度的特性。 维护根到节点 $i$ 的深度 $k$ 次方和,然后利用可减性,$f(u) + f(v) - f(lca) - f(fa(lca))$ 即可计算答案。 避免了线段树的巨大空间、常数,可以比较稳妥地通过。
|
|
[USACO19DEC]Milk Visits G
对每个品种维护一棵动态开点的线段树即可。 由于动态开点,空间复杂度是 $O(n \log n)$ 的。
|
|
[USACO18OPEN]Disruption P
显然可以区间取 min 来维护。还可以按边从大到小排序后直接区间赋值。 然而还有更好的做法,贪心排序后,对路径进行类似染色的做法,遇到之前染过色的区间就跳过,随便搞搞即可。 具体题解看 这篇。
[SHOI2012]魔法树
显然是树剖的板子,然而还可以用树上差分做到一个 $log$,据说是当年设置的正解。 考虑 $v_i$ 代表根节点到点 $i$ 上的路径,都加上了 $v_i$,那么对路径加权可以看做: $v_u := v_u + val$ $v_v := v_v + val$ $v_{lca} := v_{lca} - val$ $v_{fa_{lca}} := v_{fa_{lca}} - val$ 其实就是常规的树上差分的套路。 那么修改解决了,现在做询问。 有点类似树状数组区间修改区间查询的套路。 对于子树内的某一点 $x$,对答案的贡献为: $v_x \times (dep_x - dep_u + 1)$ 总答案贡献就是: $\sum v_x \times dep_x - (dep_u - 1) \times \sum v_x$ 用两个树状数组维护即可。
|
|
结语
虽然说本来要写轻重链剖分的笔记,但却发现许多树剖习题都有着更为优秀的ad-hoc解法(