CDQ 从二维偏序到三维偏序 从逆序对到动态逆序对详解
CDQ 分治思想
CDQ分治,顾名思义,这个思想重点在于如何 分 治 。
分:将问题从整体分解成几个小部分(一般是两个)。
治:考虑各部分之间的贡献并计算,把部分合并到整体。
二维偏序 (逆序对)
二维偏序模板问题:
一个平面上,给出n个点(X,Y)。
定义: 设 A,B 两点,如果A.x<B.x && A.y<B.y 则A点对B点产生贡献。
求每个点的权值。
最方便的解决办法当然是树状数组,对其中一维(x)排序,另一维(y)在以x的顺序插入树状数组中时,查询答案即可。
这里不累述。
还有一种方法就是CDQ分治:
附上自己写的伪代码:
void CDQ(int l,int r)
{
if(r-l<1) return; // 结束条件
/*
分
预处理排序x;
此时二分处理时左右区间左区间x全部小于右区间的x
所以右区间不会对左区间产生贡献
*/
int m = (l+r)>>1; // 二分处理
CDQ(l,m); // 递归处理左边界
CDQ(m+1,r); // 递归处理右边界
/*
治
合并区间时,由于递归处理,此时左右两部分的y是有序的 见下
而且在分的时候x是有序的
所以只有左区间会对右区间产生贡献
我们便可以在线性时间内处理左区间对右区间的贡献并以y排好序
*/
int p = l , q = m+1 , t = 0;
while(q<=r || p<=m){
/*
指针移动法
处理左区间对右区间的贡献;
并把排好序的结构体数组付给中间数组temp
*/
}
t = 0;
for(int i=l;i<=r;i++) // 整体排序 所以说上面合并时已经有序
z[i] = temp[++t];
}
给出例题 : HDU 1541
三维偏序 (动态逆序对)
在刚刚二维偏序的基础上我们给定n个点加入平面的顺序,每个点只考虑当前时间的点对它产生贡献,不考虑后加的点。
这时,应该扩充平面的点一个时间元素t,设定第一次加入点时t=1,往后时间t增加,一直到n。
这时A对B产生贡献,不仅需要A.x<B.x && A.y<B.y,还需要A.t<B.t,即B点存在时A点必定存在。
这便是一个三维偏序问题
那么我们如何处理三维偏序
我们已经知道 分治+排序 与 树状数组+排序 可以解决二维偏序问题, 排序一维+分治一维 = 二维 , 排序一维 + 树状数组一维 = 二维 ,那我们能否 排序一维 + CDQ一维 + 树状数组一维 = 三维 ?
不可以
答案当然是可以
我大致讲一下: 首先排序使x有序,然后进行CDQ分治,其基本还是和二维偏序时操作一样,但是在整体合并的处理上,不是直接查找,而是以y的顺序插入到树状数组当中,然后以t查找。
这是大致思维
更高维的偏序问题我未接触过,但是应该和我上述一样,升维即可,树套树,CDQ套CDQ等等…
动态逆序对 HYSBZ - 3295 包含 AC代码
#include<bits/stdc++.h>