树分治学习笔记
前言
既然序列可以分治,那么树也可以分治。树上的分治可以分为点分治与边分治。
点分治
边分治主要用于处理树上路径问题。
考虑一个分治的过程:选中一棵树的根,计算经过根的路径的贡献,然后以其子结点为根对子树递归地计算贡献。容易发现,在构造数据下这种算法的复杂度是可以达到 \(O(n^2)\) 的,原因在于递归的层数可能太深了。如果不使用子结点,而是使用子树的重心,那么每一次子树的大小都会减半,递归层数降到了 \(O(\log n)\) 级别。而每一层的点数是 \(O(n)\) 的,所以总时间复杂度变成了 \(O(nk\log n)\),其中 \(k\) 是统计贡献的复杂度。
然而从理论到实现还是需要一定距离的,因为点分治需要很多细节。
拓展:点分序列
我们可以将每次计算贡献函数里的点都加进一个序列,那么我们就得到了有 \(O(n\log n)\) 个数,称为点分序列。我们不妨在上面维护一些信息,比如说这个点到它当时的根的路径产生的贡献。这样你就通过 \(O(n\log n)\) 个区间来表示整棵树的路径信息了。可以用来做超级钢琴类似的东西。
点分树
有了点分治的基础,我们就可以方便定义点分树了。点分树的构造方式就是每一个重心向下一层递归中心连有向边,形成一棵有根树。它有两个重要的性质:
-
它的深度是 \(O(\log n)\) 级别的。
-
对于 \(u\) 和 \(v\),它们在点分树上的 \(\text{lca}\) 在原树 \(u\) 到 \(v\) 的路径上。
由第二个性质我们可以得到 \(\text{dist}(u,\text{lca})+\text{dist}(v,\text{lca})=\text{dist}(u,v)\)。其中 \(\text{dist}\) 是原树上距离,而 \(\text{lca}\) 是点分树上 \(\text{lca}\)。
有了这个重要的性质,就可以直接暴力跳祖先维护了。