点分治 学习笔记
新知识 +1。
0x00 前言
点分治适合处理大规模的树上路径信息问题。
0x01 引入
我们通过洛谷的模板题来引入点分治的概念: P3806 【模板】点分治1 :给定一棵有 n 个点的带边权树, m 次询问,每次询问树上距离为 k 的点对是否存在。
考虑以 r 为根的一棵树中的路径。这些路径可以分成两类:(1) 经过 r 的;(2) 在 r 的子树内的。发现这是一个子问题,所以我们可以每次只考虑经过 r 的路径再递归下去处理。而对于那些经过 r 的路径 \(u\rightarrow v\) ,我们可以拆成 \(u\rightarrow r\) 和 \(r\rightarrow v\) 两部分,即先计算子树的链,再把它们合并成一条完整的路径。这样,我们就得到了点分治的基本框架,即:以是否经过当前根节点为依据,将路径分为两个部分,递归地处理不经过当前根节点的路径,并尝试在只与当前子树大小有关的时间复杂度内解决经过当前根节点的路径。
0x02 实现
在具体实现计算时,我们一般有两种方法:开一个桶维护之前的链长,或把所有链求出来后双指针。两种方法各有优劣:桶胜在一手简单直观,但空间大可能开不下,且如果不及时清空可能导【数据删除】的神必错误;双指针更加快捷但更复杂,不能处理和是 k 的倍数、乘积为 k 的倍数等没有简单的单调性质的信息。当然,也可以用 BIT 维护,但这样会多一个 \(\log n\) ,不是很常用。
0x02.5 优化
点分治过程中,每一层的所有递归过程会对每个点处理一次,假设共递归 D 层,则总时间复杂度就是 \(\text{O}(Dn)\) ,在遇到链等极端情况时难以接受。为了使树的结构更均匀,若我们每次选择子树的重心作为根节点,可以保证递归层数最少,总时间复杂度为 \(\text{O}(n\log n)\) 。注意在重新选择根节点之后要重新计算子树的大小,否则可能导致【数据删除】的时间复杂度错误。
0x03 应用
P4178 Tree & P4149 [IOI2011]Race & P2634 [国家集训队]聪聪可可 & CF161D Distance in Tree & HDU4812 D Tree
五道板。
HDU5977 Garden of Eden
比较板,但是为什么要再清空一次?
CF1156D 0-1-Tree
大力分讨。
P2664 树上游戏
占坑。还没写。