0x40「数据结构进阶」例题
CDQ分治
CDQ分治,能够将动态问题转化为静态问题求解。它将操作的时间顺序作为分治的基础,每次递归操作的两部分,回溯时计算前一半的操作对后一半的询问的影响。在实际过程中,它往往用于解决二维平面的动态偏序问题,因而要与排序和树状数组结合。
例题
4701 天使玩偶
计算距离的过程中涉及到了绝对值,为了去掉绝对值符号,我们分四类讨论,即最优解位于询问点的左下,左上,右上,右下四个方向的情况。设目前的询问点为,那么,四个方向上该式的化简如下:
左下:即
左上:即
右上:即
右下:即
化简原则是将(常量)和(变量)分开,同时保证四个式子尽量相似(max函数前都为负号)。
因此,我们可以这样做(以左下为例):
1 将区间内操作对应的点的坐标按照横坐标升序排序。
2 扫描到一个点,就在树状数组中把个位置和取max,同时维护前缀和的最大值(注意,树状数组虽然无法维护区间最值,但是当所有区间的起点都为1时,由于不需要用到区间加法,所以树状数组可以胜任)。
3 扫描到一个询问,就在树状数组中查询上的最大值val,答案就是x+y-val。
上述做法中,排序满足了,树状数组控制了并维护了的最大值。
另外三个方向同理,例如左上方,只要按照横坐标升序排序,然后树状数组维护的最大值,修改位置为,查询时前缀变成即可。(也可以是)
时间复杂度:。
PS:为了保证时间复杂度与询问区间长度有关,我们每次不能建立新的树状数组,而是在每次修改后,都将操作依次撤销。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
*/
#include
#include
#include
#include
#include
#include
另外,这里附上CDQ分治的另外两道例题(洛谷 P3810 P4390)
基于值域的整体二分
例题
POJ2104 K-th Number
这是一道模板题,可以用四种方法AC。
这里介绍树状数组实现的整体二分做法。
将询问序列作为三元组表示中小的数。设solve(L,R,1,t)表示值域为[L,R]的序列,对询问序列1~t作出回答。
步骤如下:
1 mid=L+R>>1
2 利用树状数组,对于当前询问序列,统计数字序列在中小于等于mid的数有多少个,记为。
3 若,则将询问加入序列lq中,否则令,将询问加入序列rq中。
4 将lq和rq拷贝回原来的数组
5 还原树状数组
6 递归solve(L,mid,1,?)和solve(mid+1,R,?+1,t)
递归边界:L=R时,将L作为目前询问序列中所有询问的答案。
未离散化时间复杂度:(SIZE为值域大小)。
离散化时间复杂度:。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
*/
#include
#include
#include
#include
#include
#include
另外,若序列存在带修改操作(如A[x]=y),那么做如下转化:
将这个操作转化为在x位置去掉一个值为A[x]的数(记该类操作为-1),增加一个值为y的数(记该类操作为0)两次操作。然后将所有操作包括询问进行整体二分即可。(例题 BZOJ 1901/ZOJ 2112)