CDQ分治学习笔记
基本思想
分治(Divide and conquer)思想是计算机科学中举足轻重的一种思想,应用范围极为广泛。
CDQ 分治是普通分治思想的延伸,区别主要在于:普通分治的子问题之间互不影响,而 CDQ 分治中左子区间可以对右子区间有影响。
CDQ 分治应用广泛,在不同地方的体现不尽相同,实现上大体上可分为如下三步:
- 分(Divide):将原问题分为左右两个区间的子问题
- 治(Conquer):递归解决左右两个子区间
- 并(Combine):合并两子区间的答案,并且处理左区间对右区间产生的影响
可以发现,CDQ 分治的关键就在于如何处理左区间对右区间的影响,其余部分与普通分治相同。
CDQ 分治常常能够代替某些高级数据结构,并且常数更小,跑得更快。
练习
二维偏序(逆序对)
逆序对问题本质就是一个二维偏序。CDQ 分治时,为了线性统计,我们希望左右子区间的数值已经排好序了,这样双指针就可以统计。所以归并排序正好用在了这里。
复杂度:$T(n)=2\cdot T\left(\frac{n}{2}\right)+O(n)=O(n\lg n)$
1 |
|
hdu1541 Stars
求横纵坐标都更小的点的个数,二维偏序。
1 |
|
[SHOI2007]园丁的烦恼
用二维前缀和转化一下,然后就是求横纵坐标都更小的点的个数,即二维偏序问题。
1 |
|
luoguP2345 奶牛集会
先把所有奶牛按照 $v$ 排序,然后 CDQ 分治。在考虑左右子区间时,左子区间的任一 $v$ 都小于右子区间的任一 $v$,于是我们只需要依次考虑右子区间的每一个元素,计算左子区间中 $x$ 比它小的坐标之和以及 $x$ 比它大的坐标之和是多少。欲在这一步做到线性,我们希望左右区间分别是按照 $x$ 排好序了的,这样就可以用双指针的方法搞出答案。正好把归并排序套上去即可。
1 |
|
三维偏序(陌上花开)
先按第一维排序,然后 CDQ 分治:solve(l, r)
时,递归进行 solve(l, mid)
和 solve(mid+1, r)
,考虑如何统计 $l\leq i\leq mid,mid+1\leq j\leq r$ 的满足 $b_i<b_j$ 且 $c_i<c_j$ 的点对数。把所有 $[l,r]$ 区间的元素拿出来按第二维排序,遍历一遍,遇到左区间的就按第三维丢到值域树状数组里去,遇到右区间的就查询。
如果题目存在重复元素,需要去重(因为 CDQ 分治只能统计左区间对右区间的答案),丢值域树状数组的时候丢重复次数。
复杂度:$T(n)=2\cdot T\left(\frac{n}{2}\right)+O(n\lg n)=O(n\lg^2n)$
1 |
|
动态逆序对
删去一个值后,总逆序对数等于原逆序对数减去它参与构成的逆序对数。它参与构成的逆序对分两种情况:
- 位置在其前,值更大,删去时间更大
- 位置在其后,值更小,删去时间更大
这分别是两种三维偏序,CDQ 分治即可。
1 |
|
[BOI2007]Mokia
求横纵坐标以及时间都更小的点的数目,三维偏序。
以下代码中的排序采用了另一种方式(分别排序),两种都可以,看心情用咯~
1 |
|
luoguP4169 [Violet]天使玩偶/SJY摆棋子
正解是 K-D Tree,但是也可以用 CDQ 分治做。考虑如何把最近距离问题转换成三维偏序问题——每个点的左下、左上、右下、右上部分分别求最近距离,这样就是四次三维偏序问题。为方便写代码,左上、右下、右上可以通过翻转转化成左下。
这道题时限很紧,在 CDQ 分治中不采用 sort
来排序第二维,而是套用一个归并排序,可以减少不少常数,然后提交的时候吸吸氧就可以过了。
1 |
|
四维偏序(hdu5126 stars)
CDQ 套 CDQ分治。先按第一维 $a$ 排序,然后对第一维 CDQ 分治:递归回来后根据第一维的位置打上左右标记,然后按 $b$ 排序,得到一系列形如 $(L/R,b,c,d)$ 的元素且 $b$ 递增;复制一下,对第二维 CDQ 分治并在同时对第三维排序:递归回来后左子区间都是 $(L/R,L,c,d)$,右子区间都是 $(L/R,R,c,d)$,且各自区间内的 $c$ 是递增的,然后双指针把第四维丢值域树状数组里或者查询,丢的时候记得参考第一维的 $L/R$ 情况。
实现时用与 CDQ 浑然一体的归并排序而非 sort
来减少一些常数;同时清空树状数组时尽可能少清。
复杂度:$T(n)=2\cdot T\left(\frac{n}{2}\right)+O(n\lg^2n)=O(n\lg^3n)$
1 |
|