CDQ分治
CDQ 分治
归并排序
来自《算法导论》。
#include<algorithm>
#include<cstdio>
const int MAXN=1e6;
int n,a[MAXN+10],l[MAXN+10],r[MAXN+10];
long long ans;
void merge(int p,int q,int s) {
int n1=q-p+1,n2=s-q;
for(int i=1; i<=n1; i++) l[i]=a[p+i-1];
for(int i=1; i<=n2; i++) r[i]=a[q+i];
l[n1+1]=r[n2+1]=2e9;
int i=1,j=1;
for(int k=p; k<=s; k++) {
if(l[i]<=r[j]) a[k]=l[i++];
else {
ans+=n1-i+1;//求解逆序对
a[k]=r[j++];
}
}
}
void msort(int p,int s) {
if(p<s) {
int q=(p+s)/2;
msort(p,q);
msort(q+1,s);
merge(p,q,s);
}
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
msort(1,n);
printf("%lld\n",ans);
return 0;
}
CDQ分治
二维偏序:模板
#include<algorithm>
#include<cstdio>
#include<iostream>
using std::sort;
const int MAXN=1e6;
int n,tree[MAXN+10],level[MAXN+10];
struct node {
int x,y;
} a[MAXN+10];
bool cmp(node p,node q) {
return p.x<q.x||(p.x==q.x&&p.y<q.y);
}
void Add(int x,int k) {
for(; x<=MAXN; x+=x&-x) tree[x]+=k;
}
int Query(int x) {
int res=0;
for(; x; x-=x&-x) res+=tree[x];
return res;
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%d %d",&a[i].x,&a[i].y);
sort(a+1,a+1+n,cmp);
for(int i=1; i<=n; i++) {
int p=Query(a[i].y);
Add(a[i].y,1);
level[p]++;
}
for(int i=0; i<n; i++) printf("%d\n",level[i]);
return 0;
}
例题 P3810 【模板】三维偏序(陌上花开)
学习完了二位偏序,我们来学习三维偏序。
我们只要保证在有一维是有序的情况下,我们就把三维偏序转化为了二维偏序。
设参数为\(a,b,c\)。
先按照\(a\)参数排序,然后分治。
分治把一个区间\([l,r]\)变成\([l,mid]\)和\([mid+1,r]\)。
在分治内部,类似归并排序,按照\(b\)参数排序。
因为此时对于两个区间来说,\(a\)参数已经没有关系了,因为右区间的\(a\)参数不小于左区间的\(a\)参数。
那么此时考虑\(b\)参数。
把左区间和右区间合并,用双指针\(i,j\)。
当遇到\(b_i \leq b_j\)时,将\(c_i\)加入树状数组。
否则,查询小于\(c_j\)的个数,贡献答案。
最后,将合并后的数组更新。
注意要清空树状数组。
然后回溯。
注意开始要去重,因为如果两朵花属性完全相等,那他们不能互相计算到答案,一朵花只能计算顺序比他小的答案。
#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=1e5;
struct node {
int a,b,c,id;
} p[MAXN+10],tmp[MAXN+10];
int n,k;
int c[MAXN*2+10],ans[MAXN+10],cnt[MAXN+10],d[MAXN+10];
void add(int i,int x) {
for(; i<=k; i+=i&-i) c[i]+=x;
}
void clear(int i) {
for(; i<=k; i+=i&-i) c[i]=0;
}
int sum(int i) {
int ret=0;
for(; i; i-=i&-i) ret+=c[i];
return ret;
}
bool cmp1(node x,node y) {
return x.a<y.a||(x.a==y.a&&x.b<y.b)||(x.a==y.a&&x.b==y.b&&x.c<y.c);
}
void cdq(int l,int r) {
if(l==r) return ;
int mid=(l+r)/2;
cdq(l,mid);
cdq(mid+1,r);
int i=l,j=mid+1,k1=l;
while(i<=mid&&j<=r) {
if(p[i].b<=p[j].b) {
add(p[i].c,cnt[p[i].id]);
tmp[k1++]=p[i++];
} else {
ans[p[j].id]+=sum(p[j].c);
tmp[k1++]=p[j++];
}
}
while(i<=mid) tmp[k1++]=p[i++];
while(j<=r) {
ans[p[j].id]+=sum(p[j].c);
tmp[k1++]=p[j++];
}
for(int i=l; i<=r; i++) {
p[i]=tmp[i];
clear(p[i].c);
}
}
int main() {
scanf("%d %d",&n,&k);
for(int i=1; i<=n; i++) {
scanf("%d %d %d",&p[i].a,&p[i].b,&p[i].c);
p[i].id=i;
}
int m=0;
sort(p+1,p+1+n,cmp1);
for(int i=1; i<=n; ++i) {
if(p[i].a!=p[m].a||p[i].b!=p[m].b||p[i].c!=p[m].c) p[++m]=p[i];
cnt[m]++;
}
for(int i=1; i<=m; i++) p[i].id=i;
cdq(1,m);
for(int i=1; i<=m; i++) d[ans[p[i].id]+cnt[p[i].id]-1]+=cnt[p[i].id];
for(int i=0; i<n; i++) printf("%d\n",d[i]);
return 0;
}