CDQ分治与一般的分治的差别是
CDQ分治是前半部分的来处理后半部分的问题
因为先按第一关键字如果从小到大来排
前半部分的的所有的按第一关键字一定是小于后半部分的任意一个的
所以此时前半部分可以按第二关键字来排
第一关键字就没有影响了
就可处理第二关键字对第后半部分的影响
依次类推就可处理问题
下面例子是从一个大佬那转的大佬的博客(点击打开链接)
举个例子:在二维坐标系中,有n个点,m个询问。对于每个询问(x, y),求点(x1, x2) ,x1<= x && y1 <= y 有多少个。
当然,这题用树状数组就直接能写了。但是我们在这里用CDQ分治来解决一下这个问题。
我们将n个点看做是n个更新,并和m个询问放在一起。定义一个结构体用vector存起来。
void sovle1(int l, int r, vector<Struct>vec1) {//分解
//在这里将vec1[l, r]按x从小到大排序
int mid = (l + r) / 2;
solve1(l, mid);
solve1(mid + 1, r);//划分成相互独立的子问题
//将vec1[l, mid]中的更新操作 和 vec1[mid + 1, r]中的询问操作,存到一个vec2中
solve2(l, r, vec2);//左半边的更新操作和右半边的询问操作之间有联系
}
void solve2(int l, int r, vector<Struct>vec2) {//建立问题
//此时在solve2中已经不需要管x了。在vec2中所有的更新操作的x必然小于等于询问操作的x。**证是这句话启发我
//在这里将vec2[l, r]按y从小到大排序
int mid = (l + r) / 2;
sovle2(l, mid);
solve2(mid + 1, r);//独立子问题
//将vec2[l, mid]中的更新操作和vec2[mid + 1, r]中得询问操作存到一个vec3中
solve3(l, r, vec3);//
}
void solve3(int l, int r, vector<Struct>vec3) {//处理
//此时在solve3中,x和y都是已经排好序的。从前到后遍历该更新更新该询问询问就好。
int cal = 0;
for(Struct tmp: vec3) {
if(tmp.type == Update) ++cal;
else tmp.query_anser += cal;
}
}
//这段代码其中有许多可以优化的地方,但是不要在意细节,主要是为了更加方便地理解算法