cdq(陈丹琦)分治,是一种类似二分的算法。基本思想同分治:

  1. 递归,把大问题划分成若干个结构相同的子问题,直到(L==R);
  2. 处理左区间[L,mid]对右区间[mid+1,R]的影响;
  3. 合并。

它可以顶替复杂的高级数据结构,但必须离线操作。

N维偏序,就是求N个关键字下的顺/逆序对。cdq分治是这类题中常用的降维手段。

 

一维偏序

  学习归并排序时,我们了解到它的一个特性就是可以用来求逆序对。

  Luogu P1908 逆序对

void merge(int L,int R) {
    if(L == R)return;
    int mid = (L+R)/2;
    merge(L,mid);
    merge(mid+1,R);
    int idx = L;
    int i = L,j = mid+1;
    while(i <= mid&&j <= R) {
        if(a[i] <= a[j])temp[idx++] = a[i++];
        else {
            temp[idx++] = a[j++];
            cnt += mid-i+1;
        }
    }
    while(i <= mid)temp[idx++] = a[i++];
    while(j <= R)temp[idx++] = a[j++];
    for(int i = L; i <= R; i++)
        a[i] = temp[i];
}
  归并排序求逆序对  

考虑它的原理:只统计对于右面的每一个元素,左边比它大的。

两边的数列都为有序,且各自的逆序对都已经统计完了。

那么对于右边的第j个元素(j>=mid+1),如果左边的第i个元素比j大,那么i+1,i+2....到mid一定都比j大。

这里就体现了cdq分治的思想,也是多维偏序的基础。可以说,归并排序求逆序对是cdq分治的一个特例。

 

二维偏序

  除了归并排序,一维偏序也可以用树状数组解决。实际上,一部分树状数组能解决的问题,cdq分治也可以解决。

  Luogu P3374 【模板】树状数组 1

#include<iostream>
#include<cstdio>
#include<cstring>
#define MogeKo qwq
using namespace std;
const int maxn = 500005;
int n,m,opt,x,y,sum[maxn];

int lowbit(int x){
    return x & -x;
}

void update(int x,int k){
    while(x <= n){
        sum[x] += k;
        x += lowbit(x);
    }
}

int query(int x){
    int ans = 0;
    while(x){
        ans += sum[x];
        x -= lowbit(x);
    }
    return ans;
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++){
        scanf("%d",&y);
        update(i,y);
    }
    for(int i = 1;i <= m;i++){
        scanf("%d%d%d",&opt,&x,&y);
        if(opt == 1)update(x,y);
        if(opt == 2)printf("%d\n",query(y)-query(x-1));
    }
    return 0;
}
  树状数组  

相关文章: