普通的整体二分就是把操作在一维上分治(保证分治的层数为
\log V),分成两部分 S,T,满足在分治那一维当 T 产生贡献时 S 也产生了贡献。然后把 S 的贡献算出来,如果询问在 S 的贡献中产生,那就把询问分到 S 那一边,否则去除 S 贡献,分到 T 那一边,然后递归求解,复杂度的话分治的时候保证了深度,那外层的复杂度就只会多一只 \log V。
举个例子:单点修改,区间 k 小(Dynamic Rankings),按值域分治,把初始的序列看成在一个点插入上一个值,修改看成删除原来的值,再插入一个值,这样得到的 O(m) 个操作拿来分治,这里的修改和询问混一起更好,因为对于一个询问,S 造成的贡献还受时间限制,所以要按时间遍历(当然你要麻烦的归并一下我也拦不住你),修改的时候如果插入/删除的数在 S 里,就在一个树状数组里改,询问就区间求和,如果 <k,那这个询问就是在 T 集合中求得答案,否则是在 S 集合中。然后就可以分成两个操作序列,递归分治。
当然还有不太一样的整体二分。栗:WD 与地图。这个题的整体二分不是用来求解的,而是用来转化边的,因为插有向边,求强连通分量这个很麻烦,所以直接在时间轴上整体二分,然后暴力 tarjan 求连通性,在强连通分量里的边会在前半段时间中产生贡献,所以为 S 部,前半段时间中剩下的边和后半段时间的边为 T 部。
有些细节在于直接把边递归到 T 不好再跑 tarjan,所以要把端点的标号改成这一层强连通分量中的一个代表点。为啥要这样?因为要的是每个时间哪两个集合合并了,而这个是用并查集,所以在 T 部中可以直接看成把连通块缩到那个代表点上了。
具体来讲,把轻儿子都转移了相当于 g_{u,0}=\sum\limits_{v\in son_u\land v\neq x}\max(f_{v,0},f_{v,1}),g_{u,1}=\sum\limits_{v\in son_u\land v\neq x}f_{v,0},其中 x 为重儿子,g 为转移了轻儿子后的 dp 数组。这样的话改一个点的权值,可以改他的 g_1,然后求出该重链顶端为根的 f 的变化,去更新重链顶的父亲的 g 值,然后依次执行直到整棵树的根。
具体细节可以自己摸索,这个打起来不思考一下怎么写的话有点麻烦。
全局平衡二叉树
提到 ddp,提到树剖,怎么能少了这个 B 东西呢?
上述做法的复杂度为 n2^3\log^2 n,看着对 10^5 都不太友好(但是树剖天然常数小),更何况加强版。该做法的复杂度有两只 \log n 都是在求多条重链信息的,而套的线段树只是无脑的套了上去,这并不优雅,也就是一点不考虑平衡复杂度的问题。
想想重剖为什么是 \log n 的外层复杂度,因为每条轻边都会让子树的大小翻倍,那在一条重链上跳只要我们让他满足这个条件是不是每跳一下都是翻倍,总的复杂度(加上区间查)也只有 \log n 了呢?具体来讲就是对重链加权建一颗平衡树,使得每个点的左右两个儿子权值和之差尽量小,加的权就是每个点的轻儿子大小之和。