一种用来解决一类与树的形态无关的问题。
首先需要知道点分治。然后点分树就是把点分治过程变成一棵重构树。一个点的儿子就是下一层分治中选择的重心。
容易发现点分树的深度是 \(O(\log n)\) 级别的,因此查询和修改时都可以暴力跳父亲然后用数据结构维护。
由于点分树的形态已经与原树毫无关系了,因此我们能够有的性质比较有限:
- 两个点在树上的 LCA 在这两个点原树上的路径上,也即 \(dis(u,LCA)+dis(v,LCA)=dis(u,v)\)。
这个性质可以解决一类不关注树形态(比如路径之类的)的修改/查询问题。一般点分治的题加上修改或者强制在线之后就要上点分树了。
一般需要维护两个东西:自己子树内的信息和父亲关于自己子树内的信息,因为原树上这俩点没什么特别关系,直接维护会寄掉。
P6329
\(n\le 10^5\),要求支持修改一个点权值,求与一个点 \(dis\le k\) 的点的权值和。
点分树维护,每个点维护两个东西,一个 \(a_{i,d,0}\) 表示点分数上子树内的点到 \(i\) 点距离为 \(d\) 的权值和,另一个 \(a_{i,d,1}\) 表示与父亲距离为 \(d\) 的权值和。用树状数组维护前缀和。
树状数组开到 \(size_u+1\) 大小即可。
然后每次询问,从这个点开始跳,设跳到的点为 \(u\),父亲为 \(fa\)(当父亲是根的时候就不用跳了)。
设原来这个查询的点到 \(fa\) 的距离为 \(dis\)。
然后答案是初始这个点子树中到他的距离小于等于 \(d\) 的权值和,加上每一次跳,距离 \(fa\) 小于等于 \(k-dis\) 的权值和减 \(u\) 子树内距离 \(fa\) 小于等于 \(k\) 的权值和。
修改的时候直接暴力向根跳然后计算距离修改即可。
P2056
一棵树,每个点有个状态,初始全0。
要求支持切换一个点的状态(0/1切换),并且查询当前树上最远两个为0点的距离,两个点可以重复。若没有0点,输出-1。
点分树。维护三个可删除,支持查最大次大值的堆 \(a,b_{i},c_{i}\),\(c_i\) 中存 \(i\) 子树中到 \(fa_i\) 的距离,\(b_i\) 维护一个点每一棵子树中距离 \(i\) 最大的距离(也即每一棵子树仅有一个最大的距离在堆中),\(a\) 维护 \(b_i\) 的最大值与次大值之和。
然后一个可删除堆只需要两个 \(STL\) 堆,一个存放目前有的数,另一个存放目前待删除的数,每次如果两堆堆顶数相同就弹掉,就实现了可删除。手动封装下即可。缺点是常数大,不开 O2 时非常炸时间。
复杂度 \(O(n \log^2 n)\),对三个堆无脑直接模拟的话需要一个 \(5\) 以内的常数,开 \(O2\) 才能过。加点小优化不知道能不能裸着过。
标签:距离,fa,点分树,权值,维护,dis From: https://www.cnblogs.com/infinities/p/17129960.html