qian-xie-cdq-fen-zhi
浅解 cdq 分治
cdq 分治用来解决一类可以独立计算贡献的问题。
比较经典的问题有:合法点对数,简单动态规划优化等。
cdq 的一般思想是:将当前处理的区间分成两半,左右区间内部的贡献递归处理,这层只处理左右区间之间的贡献。
如果单层处理的复杂度是 \(\mathcal O(n\log n)\),总的算法复杂度就是 \(\mathcal O(n\log^2 n)\),如果是线性总的就是线性对数的。
光讲概念比较抽象,来看几道题:
- 三维偏序点对计数:给序列 \(a,b,c\),求 \(a_i<a_j,b_i<b_j,c_i<c_j\) 的 \((i,j)\) 数量。
先把三个序列按 \(a\) 排序。然后进行 cdq 分治:每次处理区间 \([l,r]\) 时,我们设 \(mid=\frac{l+r}2\)。
把区间 \([l,r]\) 分成 \([l,mid]\) 和 \([mid+1,r]\)。我们只用统计有多少满足题意的 \((i,j)\) 满足 \(i\in[l,mid]\),\(j\in[mid+1,r]\) 就好了,剩下的交给递归处理。
我们将左半边的数按照 \(b\) 排序,右半边的数也按照 \(b\) 排序。注意 此时左半边的 \(a\) 全都小于右半边的 \(a\)。因此 \((i,j)\) 是一定满足 \(a\) 的限制的。
考虑 \(b\) 的限制:我们进行一个归并,对于 \(j\) 双指针找到最大的 \(i\) 满足 \(b_i < b_j\),那么 \(i\) 之前的都是满足 \(b\) 的限制的。
最后考虑 \(c\)。我们用一个树状数组,在 \(i\) 扫过的时候将 \(c_i\) 扔进树状数组里,在算 \(j\) 的时候合法的 \(i\) 的数量就是树状数组中 \(<c_j\) 的数量。
于是就做完了,复杂度 \(\mathcal O(n\log^2n)\)。
- 三维最大权上升路径:给若干三维点,点有点权,要求路径中 \(x,y,z\) 坐标不降,求最大权路径。
可以考虑简单的 dp:\(dp_i\) 表示走到 \(i\) 号点的最大权值,转移可以从 \(x,y,z\) 都比它小的点转移过来。
这个难以用数据结构维护(除去kdt)。我们考虑 cdq,用和上题类似的方式。
考虑分治,每次进行所有左半边的点到右半边的转移。
转移条件仍然是一个三维偏序,不同的是由求和变为了求 \(dp_i\) 的最大值,霜树状数组里维护 \(dp_i\) 的最大值即可。
注意这里的递归顺序,我们要先求出左半边的 dp 值,将它们转移到右边,再在右边转移。
所以要先递归左边,处理完这层再递归右边。复杂度还是 \(\mathcal O(n\log^2n)\)。
- 二维平面,单点修改,矩形求和。
矩形求和可以差分变成求 \(x\leq x_0,y\leq y_0\) 的权值和。
这题与前面的题不同之处在于这个是动态的,但是 cdq 仍然可以得心应手的解决这类问题。
我们离线所有操作,考虑对操作序列分治,每次计算左边区间的修改对右边区间的询问的贡献。
注意这里不需要考虑左边的询问和右边的修改,它们自然会被递归处理。
和三维偏序类似地,把左边的修改按 \(x\) 排序,右边的询问也一样。
套路地进行一个归并,然后用树状数组维护 \(y\) 这一维,询问查询 \(\le y\) 的权值和即可。
由此可见,这个题目也是一个三维偏序:\(x\le x_0,y\le y_0\),还有一维是修改的时间维度。
可以看出,cdq 可以方便地把动态问题转为静态问题,
因为 分成的左区间时间都小于右区间的时间,于是不需要考虑次序问题。
在这题中,每层分治要解决的就是一个静态的二维数点问题。
我们还可以总结出 cdq 解决这类三维偏序问题的套路:先按第一维排序后分治,分治中左右区间分别按第二维排序,进行归并得到次序,最后用树状数组等数据结构维护第三维度。
- 天使玩偶:二维平面,加点,求距离 \((x,y)\) 曼哈顿距离最小的点的距离。
这仍然是一个动态问题,且这个要求的东西贡献可以独立计算。如果它的静态版本易于计算,分治一下就解决了。
这个距离不好计算,我们考虑每次只算 \((x,y)\) 左下方的点距最小值,然后反转一下 \(x,y\) 坐标,算个 \(4\) 次就好了。
那么又是一个经典的静态二维偏序。按 \(x\) 排序后相当于求之前的点满足 \(y\le y_0\) 的 \(x+y\) 最大值。
树状数组维护最大值即可。再套上cdq,复杂度 \(\mathcal O(n\log^2n)\)。
- CF848C:序列,单点修改,求每个值在区间中最后一次出现位置减去第一次出现位置的和。
转化题意:要求的等价于所有 \(pre_i\ge l\) 的 \(i-pre_i\) 的和,\(pre_i\) 表示 \(a_i\) 上一次出现的位置。
这显然是一个三维偏序问题:\(i\le r,pre_i\ge l\),以及时间维度。
每次单点修改会修改 \(\mathcal O(1)\) 个点的 \(pre\),那么把 \((i,pre_i)\) 看作二维点,就变成加点,删点,求矩形和的问题。
用 set 维护 \(pre\),直接 cdq 即可,复杂度 \(\mathcal O(n\log^2n)\)。
- P5621:给若干四维点,点有点权,要求路径中 \(x,y,z,w\) 坐标不降,求最大权路径。
这个是四维偏序的模板题。
四维偏序因为多了一维,不能再用一个 cdq 解决。我们考虑在 cdq 中套一个 cdq。
具体地,还是先按 \(a\) 排序,在第一个 cdq 中,我们将左边的点染成白色,右边的染成黑色。
然后进行第二个 cdq 分治处理整个区间。在第二个分治中,我们先不考虑 \(a\) 的限制,按照三维偏序的方法处理 \(b,c,d\) 三维的限制,最后只让白色点贡献到黑色点即可。
注意在递归过程中要维护好数组的排序。可以用复原数组的方法,也可以直接每一次 sort 一下。
复杂度是 \(\mathcal O(n\log^3n)\) 的,常数较大。
更多题目:TODO

浙公网安备 33010602011771号