点分治/点分树 学习笔记
点分治/边分治
分支,对于当前的分支中心/重心(根),统计该子树内经过当前根节点的答案以及不经过根节点的大答案。
具体来说:
- dfs 找重心,并将统计子树可能的贡献。
- 合并各个儿子的贡献
- 递归
点分树
当有修改时,每次求一遍点分治显然时间会超。考虑将点分治时每一级的重心连边,形成一棵新的树,并且这棵树的深度是 \(log\) 的。
以路径类问题为例,一般我们会选择处理出 \(u,v\) 的 Lca 再计算。但不妨选取这棵新树上的某一点,以这个点作为分割计算 \(u、v\) 的贡献。
又由于树高是 \(log n\) 的,所以直接暴力跳父亲维护就行了。
练习
模板题,用桶维护一下该子树内已经出现了的路径长度,每加入一个儿子就与前面的计算贡献。
一种显然的思路是把所有路径长度都排序做双指针,但是对于经过同一儿子的路径会被统计而这显然是不合法的(从该儿子到了根又从根往下走)。考虑直接容斥把这一部分的贡献减去即可。
一道 毒瘤 好题,按颜色计算贡献。对于这篇题解给出一点我自己的理解。对于第一个贡献的计算显然没有问题,这也是当前递归中心在自己子树内的答案,无需其他操作。
对于它的儿子,贡献为这个大的子树减去儿子所在子树的答案。这一块往下递归时使用像全局的一个桶记录一下从根到这的路径上有没有第一次出现的颜色,有就加贡献,回溯的时候清除就好了。
本题有 \(O(n)\) 的非点分治做法,由于这是点分治学习笔记,可以翻阅题解自行了解。
呃边分治模板。但是并查集思路更显然也更好做。考虑从大到小加边,用并查集维护联通块以及直径。注意到当你合并两个联通块时,新的直径的两个端点一定在原来的联通块的四个直径端点之间产生。然后就做完了。
模板题,在重构的树上暴力跳父亲,对于当前节点 \(u\),需要计算它的子树内距离小于等于 \(k\) 的点的贡献,以及该子树内到 \(u\) 父亲的距离小于等于 \(k\) 的点,因为在计算 \(fa[u]\) 的贡献时,\(u\) 这棵子树的已经被算过了,所以容斥消掉重复计算的部分。
点分树,首先这个东西长得就很像直径,考虑记录点分树上节点 \(u\) 的最大深度和次大值,然后把这个东西扔到两个大根堆里面维护。
一个 Trick:
用一个两个大根堆A和B构成的玩意就可以维护。删除的时候并不考虑A的删除,而是把这个数插到B内,查最大值的时候如果发现A的堆顶和B的堆顶一样就说明这个数被删除过了,就同时弹掉。
这题还有括号序列做法,可以参考这篇:https://www.luogu.com.cn/article/rfrva2vw

浙公网安备 33010602011771号