点分树学习笔记

关于点分树的一些理解

定义与性质

点分树,就是把点分治中的每一次的重心连起来,构成一棵树。
由于重心的性质,点分治最多有 \(\lg n\) 层, 所以点分树的树高最多是 \(\lg n\)

维护

一般维护两个值 \(s1\) 和 \(s2\) ,\(s1\) 表示这个点的子树对于这个点的贡献, \(s2\) 表示这个点的子树对于这个点的父亲的贡献。\(s2\) 用于对 \(s1\) 进行容斥

每次修改时要在点分树上不断跳父亲,修改点分树上父亲的信息。

每次查询时要在点分树上不断跳父亲,询问点分树上父亲的信息,一般要用到容斥。用 \(s2\) 容斥 \(s1\)

具体看了题就理解了。

用途

一般用于静态条件下点分治可解,但是强制在线或带修的题。

维护树上点和点之间的关系, 树上的全局信息。

减少递归层数或限制一个点为根,计算过这个点的点对信息。

注意事项

点分树上的位置关系,距离关系,父子关系,祖孙关系,和原树上不同。

luogu6329震波

标签

点分治, 树状数组

思路

先考虑不带修的情况,那这就是道点分治裸题,但是这道题要求支持修改且强制在线。那么点分治就不能用了。

所以我们要用点分树。

对于每一个节点维护两棵树状数组 (\(vector\) 存) , \(s1\) 维护距离当前点距离为 \(k\) 的点的价值和, \(s2\) 维护距离当前点的点分树上的父亲距离为 \(k\) 的点的价值和。

对于修改操作,改成增加,然后在点分树上不断跳父亲,修改 \(s1\) 和 \(s2\) 。

对于查询操作,在点分树上不断跳父亲,设询问点和父亲的距离为 \(d\) , 那么对于每一层,把答案加上 \(s1[fa].query(k - d) - s2[u].query(k - d)\) 用于去重,因为当前子树已经被计算过了。

维护两棵树状数组多麻烦啊,直接 \(s1[fa].query(k - d) - s1[u].query(k - d - dis(u, fa))\) 或者 \(s1[fa].query(k - d) - s1[u].query(k - dis(u, x))\) 多方便,然而要注意 点分树上的位置关系,距离关系,父子关系,祖孙关系,和原树上不同。 所以这样做是错误的。

Code

[ZJOI2007]捉迷藏

标签

点分治,堆

思路

这道题实际上是要求动态维护树的直径。

\(LCT\)大法好 ,考虑点分树解决。

像静态树直径一样,需要对于每个点维护从这个点伸入子树的最长链,和伸入不同子树的最长链和次长链,支持插入和删除,可以用可删堆实现。

对于每个点维护两个堆, \(s1\) 维护从这个点的子树伸向这个点的链, \(s2\) 维护从这个点的子树伸向这个点的点分树上的父亲的链。把这个点子节点的 \(s2\) 的堆顶插入到一个堆,这个堆就是这个点的 \(s1\) .

维护一个 \(ans\) 表示答案的堆,把 \(s1\) 的堆顶和次堆顶加起来,插入一个堆,这个堆就是 \(ans\) , 因为是动态的,支持删除, 所以要用堆维护。

关于修改,没什么好说的,直接维护就好, 删除原来的答案,加入改变后的答案.

Code

[ZJOI2015]幻想乡战略游戏

标签

点分治

思路

一句话题面: 动态维护树的带权重心。

对于每一次修改,求一遍重心,复杂度显然是 \(O(n^2)\) 。 发现这道题保证每个点的度数不超过 \(20\) 所以只要限制层数,那么就可以优化复杂度。考虑用点分治,使层数为 \(O(\lg n)\) 。

定义一个点的带权距离和表示树上所有点,到这个点的带权距离之和

根据重心的一些性质, 对于一个点,从它到重心,每个点的带权距离和一定是单调的。

所以从点分树的根开始,枚举它在原树中的每个子节点,考虑能不能更优,如果更优,递归到这个子节点所在联通块的重心。直到找不到更优解为止.

那么需要考虑如何判断是否更优,即如何查询一个点的带权距离和。 对于每个点,在点分树上维护三个值 \(s1\) , \(s2\), \(s\)

\(s1\) : 这个点的子树中的点到它的带权距离和。

\(s2\) : 这个点的子树中的点到它点分树上的父亲的带权距离和。

\(s\) : 这个点的子树的带权大小

修改很套路,查询暴力跳 \(fa\) , 一个点父亲对带权距离和的贡献就是 \((s1[fa] - s2[u]) + (s[fa] - s[u]) \times dis(fa, x)\)

Code

posted @ 2021-07-12 20:20  youwike  阅读(78)  评论(0)    收藏  举报