CDQ复习
CDQ
先从简单的二维数点入手,每个点有 \(a_i,b_i\) 两个权值,要求查询 $ i,j $ 满足\(a_i<a_j,b_i<b_j\) 的数对个数。
我们考虑先对第一维 \(a\) 排序,顺序遍历,那我们此时只需要知道前 \(i-1\) 个数中有多少个满足 \(b_k<b_i\) 即可,那我们就可以用树状数组在遍历过程中维护即可。
三维偏序问题,给定若干点,每个点有 \(a_i,b_i,c_i\) 三个权值,要求查询 \(i ,j\) 满足\(a_i<a_j,b_i<b_j,c_i<c_j\) 的数对个数。
相较二维数点问题增加了一维,还是采用前面的思想把前若干维固定,对最后一维用数据结构进行维护。CDQ算法是基于一种分治思想的解决该类问题的方法,还是考虑先对 \(a\) 进行排序,对 \(b\) 这一维进行分治处理,在分治过程中用树状数组对 \(c\) 进行维护。
具体一点来说分治过程就是,将区间一分为二,计算左区间对右区间产生的贡献,显然此时满足左区间的 \(a\) 始终小于右区间,我们分别对两个区间内部按 \(b\) 排序, 用两个指针维护对于右区间的每一位能产生贡献的左区间的范围。时间复杂度为 \(O(n\ log ^2n)\)
CDQ算法是一种比较经典的分治思路,其中的分治思想也可以应用在很多其他方面。
CDQ的一些扩展
四维偏序,还是采用原来的思路对 \(d\) 也进行分治,我们还是只计算左区间对右区间的贡献,那我们把左区间的打上 \(1\) 的标记,右区间标记为 \(0\) ,我们在对剩下的进行三维偏序,只计算标记 为 \(1\) 的对标记为 \(0\) 的贡献。时间复杂度 \(O(n\log^3 n)\)
在回到三维偏序上来,\(O(n \log^2 n)\) 的复杂度还是太慢了,是不是可以更优,我们考虑容斥,设集合 \(S_1=\{(i,j)|a_i<a_j\},S_2=\{(i,j)|b_i<b_j\},S_3=\{(i,j)|c_i<c_j\}\) 那我们要求的就是 \(|S_1\cap S_2 \cap S_3|\)
\(A=|S_1\cap S_2|+|S_1\cap S_3|+|S_2\cap S_3|\) ,相当于进行了三次二维数点
然后我们可以发现
- 对于组三维都偏序的,我们算了三遍
- 对于只有两位偏序的,我们算了一遍
- 对于只有一维偏序的,没有计算
- 对于不偏序的,没有计算
由对称性可得,前两种数对的和,等于后两种数对之和也就是 \(\frac{n*(n-1)}{2}\) 。
对称性的解释,数对(i,j)若为偏了三维的,则(j,i)一维都不偏,(i,j)为偏了两维的,则(j,i) 偏了一维
那我们最终答案就为 \(\frac{A-\frac{n*(n-1)}{2}}{2}\)
我们由此就得到了一个时间复杂度为 \(O(n\log n)\) 的方法来解决三维偏序,同时四位偏序也降到 \(O(n\log^2n)\)
同时CDQ也会较多的应用在一些DP的转移过程中,像斜率优化之类的。
例题
星之统治者有一个星盘,其可以被抽象为一棵根节点为 1 的树。树上每个节点 \(i\) 有一颗红星、一颗蓝星,亮度分别记为 \(Red_i,Blue_i\)。
现在,星之统治者想要知道,对于每个节点 \(x\),其子树内(不包括该节点)有多少节点满足:其红星亮度小于等于 xx 的红星亮度,且其蓝星亮度小于等于 \(x\) 的蓝星亮度。
你需要按编号顺序依次输出每个节点的答案。为减少输出量,如果答案为 0 则不必输出
树上问题我们考虑转化到序列上,子树问题我们就可以用dfs序来转换,问题就变成了查询 \(a_i<a_j,b_j<b_j,dfs_j<dfs_i\leq dfs_j+size_j-1\)
直接CDQ即可。

浙公网安备 33010602011771号