前置技能:归并排序,树状数组。
cdq分治主要是用来离线解决一些奇怪的问题的。可以用来代替一些高级数据结构比如树套树或者KD-Tree之类的。。。
话说挑战2上的KD-Tree我到现在还没开始学。。。
cdq遇到在线的好像就死掉了?(雾
目前在博主的能力范围内:
主要用来解决多维(三维)偏序问题。
bzoj 陌上花开:给n朵花,每朵花有abc三个属性,问对于每朵花 i 满足 ai>=aj&&bi>=bj&&ci>=cj的花儿有多少。
我们使用cdq减掉一维同时复杂度乘以log。
回想归排求逆序对,其实也就是二维偏序问题,我们在对x排好序的前提下,x可以当成下标,求 xi<xj&&yi>yj的数的数目。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int n,a[500005],b[500005];ll ans; void cdq(int l,int r){ if(l==r) return; int m=l+r>>1; cdq(l,m); cdq(m+1,r); int i=l,j=m+1,st=l; while (i<=m&&j<=r){ if(a[i]<=a[j]){ b[st++]=a[i++]; } else{ ans+=(m-i+1); b[st++]=a[j++]; } } while (i<=m) b[st++]=a[i++]; while (j<=r) b[st++]=a[j++]; for(int i=l;i<=r;i++){ a[i]=b[i]; } } int main(){ ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; cdq(1,n); cout<<ans<<endl; }
然后维护左区间对右区间每个数的影响,也就是 ans+=(m-i+1);这句话。
那么我们知道bit也可以解决二维偏序问题,所以我们使用 归并+bit就可以解决三维偏序问题。
关键代码处加了注释
#include <bits/stdc++.h> using namespace std; const int N = 2e5+5; struct Flower{ int a,b,c,cnt,ans; }a[N],A[N]; bool cmp2(const Flower &a, const Flower&b){ return a.a<b.a||(a.a==b.a&&a.b<b.b)||(a.a==b.a&&a.b==b.b&&a.c<b.c); } bool cmp1(const Flower& a,const Flower& b){ return a.b<b.b||(a.b==b.b&&a.c<b.c); } int n,k,c[N]; int lowbit(int k){ return k&-k;} void add(int pos,int num){ while (pos<=k){ c[pos]+=num;pos+=lowbit(pos); } } int sum(int x){ int ans = 0; while (x){ ans+=c[x];x-=lowbit(x); } return ans; } Flower t[N]; void cdq(int l,int r){//l-r满足a非严格递增 if(l==r) return; int m = l+r>>1; cdq(l,m); cdq(m+1,r); int j=l; for(int i=m+1;i<=r;i++){ for(;j<=m&&A[j].b<=A[i].b;j++) add(A[j].c,A[j].cnt); A[i].ans+=sum(A[i].c); } for(int i=l;i<j;i++) add(A[i].c,-A[i].cnt); int l1=l,l2=m+1; int pos=l; while(l1<=m||l2<=r) { if(l2>r||(l1<=m&&cmp1(A[l1],A[l2]))) t[pos++]=A[l1++]; else t[pos++]=A[l2++]; } for(int i=l;i<=r;i++) A[i]=t[i]; } int ans[N]; int main(){ ios::sync_with_stdio(false); cin>>n>>k; for(int i=1;i<=n;i++){ cin>>a[i].a>>a[i].b>>a[i].c; a[i].cnt=1; } sort(a+1,a+1+n,cmp2);//先保证a的大小关系 int cnt = 1; for(int i=1;i<=n;i++){ if(i==1||!(a[i].a==a[i-1].a&&a[i].b==a[i-1].b&&a[i].c==a[i - 1].c))//处理abc三个属性全部一样的花 A[cnt++] = a[i]; else A[cnt-1].cnt++; } cdq(1,cnt-1); for(int i = 1; i <= cnt; i++) ans[A[i].ans+A[i].cnt-1] += A[i].cnt; for(int i = 0; i < n; i++) cout<<ans[i]<<endl; return 0; } /** 10 3 3 3 3 2 3 3 2 3 1 3 1 1 3 1 2 1 3 1 1 1 2 1 2 2 1 3 2 1 2 1 */ /** 3 1 3 0 1 0 1 0 0 1 */
SHOI2007 园丁的烦恼
做法很多。。。这里讲一下cdq。和上一道题是一样的吧,可以这样想,把 t,x,y当成三个变量,t代表了操作时间,可以理解成 查询/添加,
然后对于 (x1,y1)到(x2,y2)可以差分一下,en...
那么这道题就变成了一个 求 满足 ti>tj,xi>=xj,yi>=yi 这样的一个三维偏序问题
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 35e5+5; 5 struct Node{ 6 int op,x,y,w,id; 7 }a[N],A[N]; 8 int n,m,up,tot; 9 int c[N]; 10 int lowbit(int x){ 11 return x&-x; 12 } 13 void upd(int pos,int x){ 14 while (pos<=up) { 15 c[pos]+=x; 16 pos+=lowbit(pos); 17 } 18 } 19 int sum(int x){ 20 int res = 0; 21 while (x){ 22 res+=c[x]; 23 x-=lowbit(x); 24 } 25 return res; 26 } 27 void clear(int x){ 28 while(x<=up) { 29 if(c[x]) c[x]=0; 30 else break; 31 x+=lowbit(x); 32 } 33 } 34 bool cmp(Node a,Node b){ 35 return a.x<b.x||(a.x==b.x&&a.op<b.op); 36 } 37 int ans[N]; 38 void cdq(int l,int r){ 39 if(l==r) return; 40 int mid = l+r>>1; 41 cdq(l,mid); 42 cdq(mid+1,r); 43 int i=l,j=mid+1,st=l; 44 while (i<=mid&&j<=r){ 45 if(cmp(a[i],a[j])){ 46 if(a[i].op==0) 47 upd(a[i].y,1); 48 A[st++]=a[i++]; 49 } else{ 50 if(a[j].op==1) 51 ans[a[j].id]+=a[j].w*sum(a[j].y); 52 A[st++]=a[j++]; 53 } 54 } 55 while (i<=mid) A[st++]=a[i++]; 56 while (j<=r){ 57 if(a[j].op==1) 58 ans[a[j].id]+=a[j].w*sum(a[j].y); 59 A[st++] = a[j++]; 60 } 61 for(int i=l;i<=r;i++){ 62 clear(a[i].y); 63 a[i]=A[i]; 64 } 65 } 66 void ins(int op,int x,int y,int w,int id){ 67 tot++; 68 a[tot].op=op;a[tot].x=x;a[tot].y=y;a[tot].w=w;a[tot].id=id; 69 } 70 int main(){ 71 scanf("%d%d",&n,&m); 72 int x,y; 73 for(int i=1;i<=n;i++){ 74 scanf("%d%d",&x,&y);;x++;y++; 75 ins(0,x,y,0,0); 76 up = max(y,up); 77 } 78 int x2,y2; 79 for(int i=1;i<=m;i++){ 80 scanf("%d%d%d%d",&x,&y,&x2,&y2); 81 x++;y++;x2++;y2++; 82 ins(1,x2,y2,1,i); 83 ins(1,x-1,y2,-1,i); 84 ins(1,x2,y-1,-1,i); 85 ins(1,x-1,y-1,1,i); 86 up = max(max(y,y2),up); 87 } 88 cdq(1,tot); 89 for(int i=1;i<=m;i++) 90 printf("%d ",ans[i]); 91 return 0; 92 }
bzoj 3295 动态逆序对
这类问题我们有一个方法就是倒着来一遍吧。然后我们按照时间戳给所有元素标号,每个点 具有 t,x,y 三个属性,就又变成了一个三维偏序问题。
那么我们需要求的 就是 满足(令当前元素为i) t<ti,x<xi,y>yi的数目加上 t<ti, x>xi,y<yi的数目。
然后我们在归并的时候分别从左往右扫,从右往左扫一下就可以了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5+5; 4 struct Node{ 5 int t,x,y; 6 }a[N],A[N]; 7 int ld[N],yx[N]; 8 int n,m,c[N]; 9 int lowbit(int x){ return x&-x;} 10 void upd(int pos,int x){ 11 while (pos<=n){ 12 c[pos]+=x; 13 pos+=lowbit(pos); 14 } 15 } 16 void clear(int x){ 17 while (x<=n){ 18 if(c[x]) c[x]=0,x+=lowbit(x); 19 else break; 20 } 21 } 22 int sum(int x){ 23 int res = 0; 24 while (x){ 25 res+=c[x]; 26 x-=lowbit(x); 27 } 28 return res; 29 } 30 void cdq(int l,int r){//保证x单调增 31 if(l==r) return; 32 int mid = l+r>>1; 33 int s1=l,s2=mid+1; 34 for(int i=l;i<=r;i++){//左边的t全部小于右边的,同时x单调 35 if(a[i].t<=mid) 36 A[s1++]=a[i]; 37 else 38 A[s2++]=a[i]; 39 } 40 for(int i=l;i<=r;i++) 41 a[i]=A[i]; 42 s1=l;s2=mid+1; 43 while (s2<=r){// 44 while (s1<=mid&&a[s1].x<a[s2].x){//x小y大 45 upd(a[s1].y,1);s1++; 46 } 47 ld[a[s2].t]+=(s1-l)-sum(a[s2].y);//左边y比他大的 48 s2++; 49 } 50 for(int i=l;i<=mid;i++) 51 clear(a[i].y); 52 s1=mid;s2=r; 53 while (s2>=mid+1){ 54 while (s1>=l&&a[s1].x>a[s2].x){//x大y小 55 upd(a[s1].y,1); 56 --s1; 57 } 58 yx[a[s2].t]+=sum(a[s2].y-1); 59 s2--; 60 } 61 for(int i=l;i<=mid;i++) 62 clear(a[i].y); 63 cdq(l,mid);cdq(mid+1,r); 64 } 65 int pos[N]; 66 long long ans[N]; 67 int main(){ 68 ios::sync_with_stdio(false); 69 cin>>n>>m; 70 for(int i=1;i<=n;i++){ 71 cin>>a[i].y;a[i].x=i;pos[a[i].y]=i; 72 } 73 int x,all=n; 74 for(int i=1;i<=m;i++){ 75 cin>>x; 76 a[pos[x]].t=all--; 77 } 78 for(int i=1;i<=n;i++){ 79 if(!a[i].t) 80 a[i].t=all--;// 81 } 82 cdq(1,n); 83 for(int i=1;i<=n;i++){ 84 ans[i]=ans[i-1]+yx[i]+ld[i]; 85 } 86 for(int i=n;i>=n-m+1;i--){ 87 cout<<ans[i]<<endl; 88 } 89 }