cdq(陈丹琦)分治,是一种类似二分的算法。基本思想同分治:
- 递归,把大问题划分成若干个结构相同的子问题,直到(L==R);
- 处理左区间[L,mid]对右区间[mid+1,R]的影响;
- 合并。
它可以顶替复杂的高级数据结构,但必须离线操作。
N维偏序,就是求N个关键字下的顺/逆序对。cdq分治是这类题中常用的降维手段。
一维偏序
学习归并排序时,我们了解到它的一个特性就是可以用来求逆序对。
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分治也可以解决。
#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; }