洛谷P3810 陌上花开 (cdq)
最近才学了cdq,所以用cdq写的代码(这道题也是cdq的模板题)
这道题是个三维偏序问题,先对第一维排序,然后去掉重复的,然后cdq分治即可。
为什么要去掉重复的呢?因为相同的元素互相之间都能贡献,而cdq过程中只能左边贡献右边的,所以要去重。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=200005; 4 struct node{ 5 int a,b,c,cnt,ans; 6 }s1[N],s2[N]; 7 int n,k,mx,m,top,su[N]; 8 int c[N];//树状数组 9 10 bool cmp1(node x,node y){//按a排序 11 if(x.a==y.a){ 12 if(x.b==y.b) return x.c<y.c; 13 else return x.b<y.b; 14 } 15 else return x.a<y.a; 16 } 17 18 bool cmp2(node x,node y){//cdq分治过程中对b排序 19 if(x.b==y.b) return x.c<y.c; 20 else return x.b<y.b; 21 } 22 23 int lowbit(int x){ 24 return x&(-x); 25 } 26 27 void add(int x,int k){ 28 while(x<=mx){ 29 c[x]+=k; 30 x+=lowbit(x); 31 } 32 } 33 34 int query(int x){ 35 int sum=0; 36 while(x){ 37 sum+=c[x]; 38 x-=lowbit(x); 39 } 40 return sum; 41 } 42 43 void cdq(int l,int r){//cdq 44 if(l==r) return ; 45 int mid=(l+r)>>1; 46 cdq(l,mid);cdq(mid+1,r); 47 sort(s2+l,s2+1+mid,cmp2); 48 sort(s2+mid+1,s2+r+1,cmp2); 49 int i,j=l; 50 for(int i=mid+1;i<=r;i++){//双指针计算结果 51 while(s2[i].b>=s2[j].b&&j<=mid){ 52 add(s2[j].c,s2[j].cnt); 53 j++; 54 } 55 s2[i].ans+=query(s2[i].c);//计算ans 56 } 57 for(int i=l;i<j;i++){//清空数组 58 add(s2[i].c,-s2[i].cnt); 59 } 60 } 61 62 int main() 63 { 64 scanf("%d%d",&n,&k); 65 mx=k; 66 for(int i=1;i<=n;i++){ 67 int a,b,c; 68 scanf("%d%d%d",&a,&b,&c); 69 s1[i].a=a;s1[i].b=b;s1[i].c=c; 70 } 71 sort(s1+1,s1+1+n,cmp1); 72 for(int i=1;i<=n;i++){//去掉重复的 73 top++; 74 if(s1[i].a!=s1[i+1].a||s1[i].b!=s1[i+1].b||s1[i].c!=s1[i+1].c){ 75 m++; 76 s2[m].a=s1[i].a;s2[m].b=s1[i].b;s2[m].c=s1[i].c; 77 s2[m].cnt=top; 78 top=0; 79 } 80 } 81 cdq(1,m); 82 for(int i=1;i<=m;i++) su[s2[i].ans+s2[i].cnt-1]+=s2[i].cnt; 83 for(int i=0;i<n;i++) 84 cout<<su[i]<<endl; 85 return 0; 86 }