CDQ解决一些三维偏序的问题
本来几天前就该记录的东西,硬生生被我拖到了现在,太懒了。。。
在cdq学习时,二维偏序已经解决了,无非就是先sort使第一维有序,然后再用cdq或者数据结构处理第二维。而三维偏序的时候呢,大佬的做法好像有树套树之类的,但是我不会,所以我选择cdq。
大体的思路很简单,首先也是先使第一维有序,然后cdq把第二维由小到大合并,这时再套个线段树或者树状数组处理第三维。来几题做一下
题目大意:一个花园里有n棵数,然后有m次询问,每次询问一个矩形里有多少颗树。
把原有的树视为更新操作,然后每个查询分为加上(0,0)到(x1-1,y1-1)范围内的树,减去(0,0)到(x1-1,y2)范围内的树,减去(0,0)到(x2,y1-1)范围内的树,加上(0,0)到(x2,y2)范围内的树,这样就可以处理一个二维前缀和,然后用cdq分治理套树状数组处理y轴
1 #include<cstdio> 2 #include<algorithm> 3 #define lowb(x) (x&(-x)) 4 using namespace std; 5 const int N=500118,M=500118,Y=10000118,Q=(M<<2)+N;//每个查询分4个 6 struct Nop{ 7 int x,y,op,w,id; 8 friend bool operator < (const Nop &n1,const Nop &n2){ 9 return n1.x==n2.x ? n1.op<n2.op : n1.x<n2.x; 10 } 11 }P[Q],temp[Q]; 12 int pn,maxy,ans[M<<1],sumy[Y]={0}; 13 void addp(int x,int y,int op,int w,int id){ 14 P[pn++]=(Nop){x,y,op,w,id}; 15 } 16 void updata(int y) 17 { 18 while(y<=maxy) 19 { 20 sumy[y]++; 21 y+=lowb(y); 22 } 23 } 24 int getsum(int y) 25 { 26 int sum=0; 27 while(y) 28 { 29 sum+=sumy[y]; 30 y-=lowb(y); 31 } 32 return sum; 33 } 34 void clear(int y) 35 { 36 while(y<=maxy) 37 { 38 if(sumy[y]) 39 sumy[y]=0; 40 else 41 break; 42 y+=lowb(y); 43 } 44 } 45 void cdq(int l,int r) 46 { 47 if(l==r) 48 return ; 49 int m=(l+r)>>1; 50 cdq(l,m); 51 cdq(m+1,r); 52 int i=l,j=m+1,k=l; 53 while(i<=m&&j<=r) 54 { 55 if(P[i]<P[j]) 56 { 57 if(P[i].op==0) 58 updata(P[i].y); 59 temp[k++]=P[i++]; 60 } 61 else 62 { 63 if(P[j].op==1) 64 ans[P[j].id]+=P[j].w*getsum(P[j].y); 65 temp[k++]=P[j++]; 66 } 67 } 68 while(i<=m) 69 temp[k++]=P[i++]; 70 while(j<=r) 71 { 72 if(P[j].op==1) 73 ans[P[j].id]+=P[j].w*getsum(P[j].y); 74 temp[k++]=P[j++]; 75 } 76 for(i=l;i<=r;i++) 77 { 78 clear(P[i].y); 79 P[i]=temp[i]; 80 } 81 } 82 int main() 83 { 84 int n,m,x1,y1,x2,y2; 85 while(~scanf("%d%d",&n,&m)) 86 { 87 pn=maxy=0; 88 for(int i=0;i<n;i++) 89 { 90 scanf("%d%d",&x1,&y1); 91 x1++,y1++; 92 addp(x1,y1,0,0,0); 93 maxy=max(maxy,y1); 94 } 95 for(int i=0;i<m;i++) 96 { 97 ans[i]=0; 98 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 99 x1++,y1++,x2++,y2++; 100 //拆分成四个查询 101 addp(x1-1,y1-1,1,1,i); 102 addp(x1-1,y2,1,-1,i); 103 addp(x2,y1-1,1,-1,i); 104 addp(x2,y2,1,1,i); 105 maxy=max(maxy,max(y1,y2)); 106 } 107 cdq(0,pn-1); 108 for(int i=0;i<m;i++) 109 printf("%d\n",ans[i]); 110 } 111 return 0; 112 }
当然,这题很明显是个二维偏序问题,因为树本身都存在了,没有什么更新操作,直接离线树状数组处理就行。
1 #include<cstdio> 2 #include<algorithm> 3 #define lowb(x) (x&(-x)) 4 using namespace std; 5 const int N=500118,M=500118,Y=10000118,Q=(M<<2)+N; 6 struct Nop{ 7 int x,y,op,w,id; 8 friend bool operator < (const Nop &n1,const Nop &n2){ 9 return n1.x==n2.x ? n1.op<n2.op : n1.x<n2.x; 10 } 11 }P[Q]; 12 int pn,maxy,ans[M<<1],sumy[Y]={0}; 13 void addp(int x,int y,int op,int w,int id){ 14 P[pn++]=(Nop){x,y,op,w,id}; 15 } 16 void updata(int y) 17 { 18 while(y<=maxy) 19 { 20 sumy[y]++; 21 y+=lowb(y); 22 } 23 } 24 int getsum(int y) 25 { 26 int sum=0; 27 while(y) 28 { 29 sum+=sumy[y]; 30 y-=lowb(y); 31 } 32 return sum; 33 } 34 void clear(int y) 35 { 36 while(y<=maxy) 37 { 38 if(sumy[y]) 39 sumy[y]=0; 40 else 41 break; 42 y+=lowb(y); 43 } 44 } 45 int main() 46 { 47 int n,m,x1,y1,x2,y2; 48 while(~scanf("%d%d",&n,&m)) 49 { 50 pn=maxy=0; 51 for(int i=0;i<n;i++) 52 { 53 scanf("%d%d",&x1,&y1); 54 x1++,y1++; 55 addp(x1,y1,0,0,0); 56 maxy=max(maxy,y1); 57 } 58 for(int i=0;i<m;i++) 59 { 60 ans[i]=0; 61 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 62 x1++,y1++,x2++,y2++; 63 addp(x1-1,y1-1,1,1,i); 64 addp(x1-1,y2,1,-1,i); 65 addp(x2,y1-1,1,-1,i); 66 addp(x2,y2,1,1,i); 67 maxy=max(maxy,max(y1,y2)); 68 } 69 for(int i=1;i<=maxy;i++) 70 clear(i); 71 sort(P,P+pn); 72 for(int i=0;i<pn;i++) 73 { 74 if(!P[i].op) 75 updata(P[i].y); 76 else 77 ans[P[i].id]+=P[i].w*getsum(P[i].y); 78 } 79 for(int i=0;i<m;i++) 80 printf("%d\n",ans[i]); 81 } 82 return 0; 83 }
题目大意:维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
S值并没有实际作用忽略就好。这题就不行像上面那题单纯的离线然后树状数组维护就行了,因为是有着一个更新的存在,不同的查询前面的更新是不一样的。所以就是我们大体的思路,把每个操作视为(操作时间,x轴,y轴)这样的带有附加消息的三维有序对,这样第一维已经有序了,我们就只需要把x轴cdq分治处理,然后y轴有树状数组处理,查询也就是维护个二维的前缀和。
1 #include<cstdio> 2 #include<algorithm> 3 #define lowb(x) (x&(-x)) 4 using namespace std; 5 const int N=160118,M=10118,Y=2001108,Q=(M<<2)+N; 6 struct Nop{ 7 int x,y,op,w,id; 8 friend bool operator < (const Nop &n1,const Nop &n2){ 9 return n1.x==n2.x ? n1.op<n2.op : n1.x<n2.x; 10 } 11 }P[Q],temp[Q]; 12 int pn,qn,maxy,ans[M]={0},sum[Y]={0}; 13 void addp(int x,int y,int op,int w,int id){ 14 P[pn++]=(Nop){x,y,op,w,id}; 15 } 16 void updata(int y,int val) 17 { 18 while(y<=maxy) 19 { 20 sum[y]+=val; 21 y+=lowb(y); 22 } 23 } 24 int getsum(int y) 25 { 26 int ans=0; 27 while(y) 28 { 29 ans+=sum[y]; 30 y-=lowb(y); 31 } 32 return ans; 33 } 34 void clear(int y) 35 { 36 while(y<=maxy) 37 { 38 if(sum[y]) 39 sum[y]=0; 40 else 41 break; 42 y+=lowb(y); 43 } 44 } 45 void cdq(int l,int r) 46 { 47 if(l==r) 48 return ; 49 int m=(l+r)>>1; 50 cdq(l,m); 51 cdq(m+1,r); 52 int i=l,j=m+1,k=l; 53 while(i<=m&&j<=r) 54 { 55 if(P[i]<P[j]) 56 { 57 if(P[i].op==1) 58 updata(P[i].y,P[i].w); 59 temp[k++]=P[i++]; 60 } 61 else 62 { 63 if(P[j].op==2) 64 ans[P[j].id]+=P[j].w*getsum(P[j].y); 65 temp[k++]=P[j++]; 66 } 67 } 68 while(i<=m) 69 temp[k++]=P[i++]; 70 while(j<=r) 71 { 72 if(P[j].op==2) 73 ans[P[j].id]+=P[j].w*getsum(P[j].y); 74 temp[k++]=P[j++]; 75 } 76 for(i=l;i<=r;i++) 77 { 78 clear(P[i].y); 79 P[i]=temp[i]; 80 } 81 } 82 int main() 83 { 84 int n,op,x1,y1,x2,y2,a; 85 while(~scanf("%d%d",&n,&n)) 86 { 87 pn=qn=maxy=0; 88 while(scanf("%d",&op)&&op!=3) 89 { 90 if(op==1) 91 { 92 scanf("%d%d%d",&x1,&y1,&a); 93 x1++,y1++; 94 addp(x1,y1,op,a,0); 95 maxy=max(maxy,y1); 96 } 97 else 98 { 99 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 100 x1++,y1++,x2++,y2++; 101 addp(x1-1,y1-1,op,1,qn); 102 addp(x1-1,y2,op,-1,qn); 103 addp(x2,y1-1,op,-1,qn); 104 addp(x2,y2,op,1,qn); 105 qn++; 106 maxy=max(maxy,max(y1,y2)); 107 } 108 } 109 cdq(0,pn-1); 110 for(int i=0;i<qn;i++) 111 { 112 printf("%d\n",ans[i]); 113 ans[i]=0; 114 } 115 } 116 return 0; 117 }
题目大意:有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),用三个整数表示。现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。
这就是个三维偏序的问题了,没有什么更新和查询就是(s,c,m)的三维有序对求顺序对,我们同样是让sort按s由小到大,s相同按c排,c相同按m排的cmp使得第一维s有序,然后cdq分治处理第二维c,树状数组维护第三维m,最后提交,好的,wrong了。因为一个重要的点,,两朵花可能有同样的属性,如果我们不去重的话,那么相同的属性的话,因为它们的顺序不同造成它们的等级不同。所以我们把属性相同的归为一类,然后统计这一类有多少个,然后作为树状数组维护的权值维护就行,有个小细节就是当分到最小的子区间也就是只有一朵花时,它的等级应该还得提升它的权值-1,因为那些和它属性相同的合为一类了,具体的就看代码注释。
1 #include<cstdio> 2 #include<algorithm> 3 #define lowb(x) (x&(-x)) 4 using namespace std; 5 const int N=100118,K=200118; 6 struct Flower{ 7 int id,s,c,m,w; 8 friend bool operator == (const Flower &f1,const Flower &f2){ 9 return f1.s==f2.s&&f1.m==f2.m&&f1.c==f2.c; 10 } 11 }F[N],temp[N]; 12 int fn,ans[N]={0},rank[N]={0},summ[K]={0}; 13 //ans[i]保存编号为i的花的等级,也就是属性小于或等于它的数目 14 bool cmp(const Flower &f1,const Flower &f2){ 15 return f1.s==f2.s ? (f1.c==f2.c ? f1.m<f2.m : f1.c<f2.c) : f1.s<f2.s; 16 } 17 void updata(int m,int val) 18 { 19 while(m<=200000) 20 { 21 summ[m]+=val; 22 m+=lowb(m); 23 } 24 } 25 int getsum(int m) 26 { 27 int ans=0; 28 while(m) 29 { 30 ans+=summ[m]; 31 m-=lowb(m); 32 } 33 return ans; 34 } 35 void clear(int m) 36 { 37 while(m<=200000) 38 { 39 if(summ[m]) 40 summ[m]=0; 41 else 42 break; 43 m+=lowb(m); 44 } 45 } 46 void cdq(int l,int r) 47 { 48 if(l==r) 49 { 50 ans[F[l].id]+=F[l].w-1;//因为相同属性的归到一类了 51 //所以还得加上F[l].w-1,也就是除它之外, 52 //其他相同属性的花的数目 53 return ; 54 } 55 int mid=(l+r)>>1; 56 cdq(l,mid); 57 cdq(mid+1,r); 58 int i=l,j=mid+1,k=l; 59 while(i<=mid&&j<=r) 60 { 61 if(F[i].c<=F[j].c) 62 { 63 updata(F[i].m,F[i].w); 64 temp[k++]=F[i++]; 65 } 66 else 67 { 68 ans[F[j].id]+=getsum(F[j].m); 69 temp[k++]=F[j++]; 70 } 71 } 72 while(i<=mid) 73 temp[k++]=F[i++]; 74 while(j<=r) 75 { 76 ans[F[j].id]+=getsum(F[j].m); 77 temp[k++]=F[j++]; 78 } 79 for(i=l;i<=r;i++) 80 { 81 clear(F[i].m); 82 F[i]=temp[i]; 83 } 84 } 85 int main() 86 { 87 int n,k; 88 while(~scanf("%d%d",&n,&k)) 89 { 90 for(int i=0;i<n;i++) 91 { 92 F[i].w=1; 93 scanf("%d%d%d",&F[i].s,&F[i].c,&F[i].m); 94 } 95 sort(F,F+n,cmp); 96 fn=0; 97 //去重 98 for(int i=0;i<n;i++) 99 { 100 if(i&&F[i]==F[i-1])//如果和前一朵花属性相同,归到一类 101 F[fn-1].w++; 102 else 103 { 104 F[fn]=F[i]; 105 F[fn].id=fn++; 106 } 107 } 108 //原先n朵花去重就只有fn朵 109 cdq(0,fn-1); 110 for(int i=0;i<fn;i++) 111 { 112 rank[ans[F[i].id]]+=F[i].w; 113 ans[F[i].id]=0; 114 } 115 for(int i=0;i<n;i++) 116 { 117 printf("%d\n",rank[i]); 118 rank[i]=0; 119 } 120 } 121 return 0; 122 }