点分树

一种用来解决一类与树的形态无关的问题。

首先需要知道点分治。然后点分树就是把点分治过程变成一棵重构树。一个点的儿子就是下一层分治中选择的重心。

容易发现点分树的深度是 \(O(\log n)\) 级别的,因此查询和修改时都可以暴力跳父亲然后用数据结构维护。

由于点分树的形态已经与原树毫无关系了,因此我们能够有的性质比较有限:

  • 两个点在树上的 LCA 在这两个点原树上的路径上,也即 \(dis(u,LCA)+dis(v,LCA)=dis(u,v)\)

这个性质可以解决一类不关注树形态(比如路径之类的)的修改/查询问题。一般点分治的题加上修改或者强制在线之后就要上点分树了。

一般需要维护两个东西:自己子树内的信息和父亲关于自己子树内的信息,因为原树上这俩点没什么特别关系,直接维护会寄掉。

P6329

Done.

\(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

Done.

一棵树,每个点有个状态,初始全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\) 才能过。加点小优化不知道能不能裸着过。

LgP3241

要求查询树上所有 \(x\) 值在 \([l,r]\) 的点和点 \(u\) 的距离之和。强制在线。

题解:简单点分治。用 vector 维护前缀和然后二分即可,复杂度 \(O(q \log ^2 n)\)

posted @ 2023-02-17 14:13  infinities  阅读(87)  评论(0编辑  收藏  举报