树分治(二)——边分治与动态点分治
边分治
和点分治类似,我们在树上取一条边,然后将路径分为「不经过这条边的路径」和「经过这条边的路径」,如图:

边 \((1,4)\) 将整棵树分为了两部分。
路径 \(3\to 2\to 8\) 不经过这条边,属于第一类路径;路径 \(2\to 1\to 4\to 6\) 经过这条边,属于第二类。
对于第一类路径递归下去计算,第二类路径和点分治类似。
每次选择两边 \(\text{size}\) 较大值尽可能小的边进行处理,递归深度就是 \(O(\log n)\)......吗?
实际上并非如此=_= 考虑一个菊花图,可以发现不管怎样进行分治,递归深度都是 \(O(n)\)。
一种解决方法是对每个点的若干个儿子,我们新建 \(\text{deg}_i-1\) 个点把这颗树变成二叉树。如图:

由于 \(\sum \text{deg}_i=O(n)\),所以我们可以加入 \(O(n)\) 个点让每个点的度数都不会太大。
但我们加入了一些点,这就会带来一些问题,比如说原树上两点的距离和新树上两点距离不相等之类的=_=
解决方法是把新加入的点边权值都设为 \(0\)。由此我们可以看到:边分治中加入的虚点虚边不能影响原树的信息统计。
当然,有付出就会有收获,边分治有很好的性质:一条边把树划分成了两个部分,这样一来,我们需要考虑的也只有两个集合,大大降低了实现难度。
边分治-例题
BZOJ2870 最长道路tree
这题有并查集+维护直径的简单做法,不过我们这里用边分治来做这道题。
考虑选一条边将树分为两个部分,\(f_u,g_u\) 分别表示 \(u\) 到分治中心对应侧节点的距离与路径上点权 \(\min\)。
那么对于当前分治中心边 \((u,v)\),设 \(S(u)\) 和 \(S(v)\) 分别为 \(u,v\) 各自侧节点所构成集合,答案就是
考虑把路径分为 \(g_x\ge g_y\) 与 \(g_x\le g_y\) 两类,对于第一类我们枚举 \(g_y\),分别将左右两侧节点按 \(g\) 排序,然后维护一个指针计算 \(x\) 侧的最大值即可。
另一种同理,于是就做完了。复杂度 \(O(n\log ^2n)\)。AC Code
动态点分治
对于一棵树 \(T\),考虑在点分治的过程中建出一棵新树 \(T_2\)。
对于点分治过程中的一个当前根 \(r\) 与其儿子 \(v\) 子树的重心 \(t\),我们在新树 \(T_2\) 上连边 \(r\to t\)。这棵新树就是「点分树」。
点分树有一些性质:
- 对于两个点 \(u,v\),其在点分树上的 LCA 必然在原树上两点的路径上。
这保证了我们点分治算法的正确性。
我们知道树上路径问题经常求出 LCA 然后把这条路径拆分成 \(u\to \text{LCA}\) 与 \(\text{LCA}\to v\) 两段,再进一步处理。
而仔细思考一下可以发现实际上并不一定真的要从 LCA 这里分开。
点分治实际上就是从点分树上的 LCA 处把路径断开,然后进行统计。
- 点分树的树高为 \(O(\log n)\)。
这一点保证了点分治算法的复杂度。
如果直接在原树上枚举 LCA,那么复杂度为 \(O(\sum \text{size}(i))=O(nh)\),其中 \(h\) 为树高。
而点分树的树高为 \(O(\log n)\),那么复杂度就愉快地变成了 \(O(n\log n)\)。
第二个性质尤为重要。
例如,我们现在要求一个点 \(x\) 到所有其他点的距离之和,即 \(\sum_v\text{dist}(x,v)\),那么考虑枚举 \(x\) 和 \(v\) 的 \(\text{LCA}\),我们在点分树上不断跳 \(x\) 的祖先 \(r\)。
对于一对父子 \((r,fa_r)\),设 \(\text{Sum}(x)\) 表示 \(x\) 子树内所有点与 \(x\) 距离之和,那么贡献就是 \(\text{Sum}(fa_r)-\text{Sum}(r)\)。
减掉 \(\text{Sum}(r)\) 的原因如果 \(x,v\) 的 \(\text{LCA}\) 为 \(fa_r\),那么 \(v\) 不可能在 \(r\) 的子树内。感觉理解普通点分治之后应该很容易理解这一点= =
由于 \(x\) 只有 \(O(\log n)\) 个祖先,该算法的复杂度是正确的。
点分树是原树的一棵重构树。注意在算贡献的过程中要分清原树和点分树=_=
动态点分治-例题
Luogu6329 震波 Present 5
仍然考虑枚举 \(x\) 的祖先 \(r\),然后计算父子对 \((r,fa_r)\) 的贡献。
设 \(f_1(u,j)\) 为 \(u\) (在点分树上的)子树内与 \(u\) (在原树上的)距离 \(\le j\) 的所有点的点权之和。
为了去除 \(u\) (在点分树上的)子树的贡献, 还需要记录 \(f_2(u,j)\) 表示 \(u\) (在点分树上的)子树内与 \(fa_u\) (在原树上的)距离 \(\le j\) 的所有点的点权之和。
你可能会疑惑:\(f_2(u,j)\) 难道不就是 \(f_1(u,j-1)\) 吗?注意这里 \(u\) 和 \(fa_u\) 在点分树上的距离(\(=1\))与在原树上的距离(未知)并不相等,因此需要分别记录。
那么查询 \(\text{ans}(x,k)\) 的时候,对于 \(x\) 的两个祖先 \((r,fa_r)\),其贡献为 \(f_1(fa_r,k-\text{dist}(x,fa_r))-f_2(r,k-\text{dist}(x,fa_r))\)。
修改的时候也是同理。
考虑怎么维护 \(f_1,f_2\):我们发现修改时相当于一个单点加,查询相当于区间求和,那么可以使用树状数组维护。使用树剖来查询 \(\text{dist}\),总的时间复杂度为 \(O(n\log ^2n)\)。
注意使用树状数组时需要动态开点,详细看代码。AC Code
Luogu2056 ZJOI2007 捉迷藏 Present 5
仍然考虑点分树,在修改的过程中动态维护答案。
我们相当于要维护树上的一个点集,支持加入/删除一个点,并查询该点集所构成虚树的直径。LCT
考虑建出点分树,当加入一个点 \(x\) 的时候,我们枚举这个点在点分树上的相邻祖先 \((r,fa_r)\) ,并计算以 \(fa_r\) 为 \(\text{LCA}\) 的所有路径 \((x,v)\) 的贡献。
这里我们要求 \(v\) 不能在 \(r\) 的子树内,但需要在 \(fa_r\) 的子树内。
我们发现上题中由于求的是一个和式所以可以直接减掉 \(r\) 子树的贡献,但本题要求的是一个 \(\text{max}\) 形式,这东西不具有可减性,怎么办呢=_=
其实点分树是很灵活的一个东西!考虑在每个点 \(r\) 处维护一个堆 \(F(r)\),里面装上其子树内所有点与 \(fa_r\) 的距离,同时在 \(fa_r\) 处维护其所有子节点 \(v\) 中 \(F(v)\) 堆顶所构成集合的最大与次大值之和即可。设该值为 \(S(r)\),那么答案就是所有 \(S(r)\) 中的最大值。
那么一个点改变颜色就相当于在堆里插入/删除一个数,使用 std::multiset 来实现堆即可......?
实测这样会 TLE 一个点,可以使用两个 priority_queue 来实现一个堆,减小常数。
具体来说,维护两个优先队列 \(Q_1,Q_2\),插入一个数 \(x\) 就直接把 \(Q_1.\text{push}(x)\),删除一个数 \(x\) 时我们把 \(x\) 扔进 \(Q_2\)。
查询堆顶的时候,如果 \(Q_1.\text{top}()==Q_2.\text{top}()\),那么就一直弹掉两个堆的堆顶直到二者不同。
为什么这样是对的?其实这种方法叫做「懒惰删除」,一般的做法是另开一个
std::map来维护已经删除的数,不过map常数仍然很大,因此可以使用另一个优先队列来实现。不难证明这样做的正确性。
P3241 HNOI2015 开店 Present 5
考虑建出点分树,对于当前的询问 \((u,L,R)\),我们在点分树上不断跳其父节点 \((r,fa_r)\),然后统计 \(fa_r\) 的贡献减去 \(r\) 的贡献。
考虑在每个点 \(x\) 处维护两个动态开点线段树 \(T_1,T_2\),对于 \(x\)(在点分树上的)子树内的每个点 \(y\) 我们在 \(T_1\) 的下标 \(A_y\) 处加上 \(\text{dist}(x,y)\),在 \(T_2\) 下标 \(A_y\) 处加上 \(\text{dist}(fa_x,y)\),那么一对父子 \((r,fa_r)\) 的贡献就是 \(fa_r\) 的线段树 \(T_1\) 中 \([L,R]\) 的区间和减去 \(r\) 的线段树 \(T_2\) 中 \([L,R]\) 的区间和。
然后你发现这个东西的空间复杂度是 \(O(n\log ^2n)\) 而且常数巨大。。
其实这题没有修改,所以在每个点维护一个 vector 并且求个前缀后缀和之类的就完事了,这样空间复杂度是 \(O(n\log n)\),常数貌似还好?AC Code
CF337D Book of Evil Present 5
相当于给树上若干个点的点权 \(+1\),然后查询有多少个点距离 \(d\) 范围内的点权之和等于 \(m\)。
建出点分树直接做就行了,时间复杂度 \(O(n\log ^2n)\)。AC Code

.
浙公网安备 33010602011771号