[DS 小计] 点分树
点分树是一个处理树上距离的优秀 DS。
它可以快速处理关于一些树上距离问题。
引入
我们知道,我们在做点分治的时候,每次找到中心,然后将重心所有的相连的边断开,处理子问题。时间复杂度是 \(O(n\log n)\) 的。
但是有些题目让我们搞强制在线,又要求距离为 \(k\) 的所有和,这时候点分树能起到一个很好的作用。
过程
构造一棵点分树的方法是,把每次点分治得到的根连起来,得到一棵虚树。
这棵树和点分治一样,有一些优秀的性质:
- 树高不超过 \(\log n\)
- 对于点分树上的 \(u,v\) ,若点分树上 \(lca\) 为 \(z\),原树上的 \(\text{dist}(u,v)=\text{dist}(u,z)+\text{dist}(v,z)\)
这个证明很简单,因为 \(u,v\) 是在 \(z\) 的这个地方断开的,所以 \(z\) 一定在原树上 \(u\to v\) 之间。要不然它是怎么断开的呢(((
点分树的两个优秀性质就在此。
来看经典问题:给定 \(u,k\) ,求所有满足 \(\text{dist}(u,v)\le k\) 的 \(w_v\) 的和。
怎么做呢?令 \(z\) 为点分树上 \(u,v\) 的 \(lca\) 。
\(\text{dist}(u,v)\le k \Leftrightarrow \text{dist}(u,z)+\text{dist}(v,z)\le k\Leftrightarrow \text{dist}(v,z)\le k-\text{dist}(u,z)\)
所以,我们暴力枚举 \(z\) 即可。
所以我们开一棵动态开点线段树,记录所有 \(\text{dist}(x,z)\) ,表示这个点所有长度的情况。
然后暴力个更新查找。
但是有来自同一子树的两个点,这不能贡献,所以容斥记录一下,再开一棵动态开点线段树,记录 \(x\) 子树内每个点到 \(x\) 点分树父亲的距离的情况即可。
如果你写的很好,空间复杂度是 \(O(n\log n)\) 的。
你知道的,笔者太菜了,实现起来太细节,所以只实现了 \(O(n\log ^2 n)\)