啃了一天论文,发现CDQ分治的原理其实很简单,大概就是这样的一类分治:将左右区间按一定规律排序后分开处理,递归到底时直接计算答案,对于一个区间,按照第二关键字split成两个区间,先处理左区间,之后因为整个区间是有序的,就可以根据左区间来推算右区间的答案,最后递归处理右区间即可。拿此题做比方,先把全区间按照x坐标排序,然后自左向右用前一半(按时间排序)的修改来更新后一半的查询,之后将整个区间按照时间分成两个部分,递归处理。归纳起来就是split->left->push->right->merge->return。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <ctime> 7 #include <cmath> 8 9 using namespace std; 10 11 struct node { int v,x,y,op,d,pos; }a[210000],temp[210000]; 12 13 int s,w,cnt; 14 int L[2100000],Ans[11000]; 15 16 bool operator<(const node temp1,const node temp2) 17 { 18 if(temp1.x!=temp2.x)return temp1.x<temp2.x; 19 if(temp1.y!=temp2.y)return temp1.y<temp2.y; 20 return temp1.op<temp2.op; 21 } 22 23 void Add(const int x,const int d) { for(int i=x;i<=w;i+=i&-i)L[i]+=d; return ; } 24 int Query(const int x) { int t=0; for(int i=x;i;i-=i&-i) t+=L[i]; return t; } 25 26 void CDQ(const int l,const int r) 27 { 28 if(l==r)return ; 29 30 int i,mid=l+((r-l)>>1); 31 32 for(i=l;i<=r;++i) 33 { 34 if(a[i].v<=mid && a[i].op==0)Add(a[i].y,a[i].d); 35 if(a[i].v>mid && a[i].op==1)Ans[a[i].pos]+=Query(a[i].y)*a[i].d; 36 } 37 for(i=l;i<=r;++i) 38 if(a[i].v<=mid && a[i].op==0)Add(a[i].y,-a[i].d); 39 40 int l1=l,l2=mid+1; 41 for(i=l;i<=r;++i) 42 { 43 if(a[i].v<=mid)temp[l1++]=a[i]; 44 else temp[l2++]=a[i]; 45 } 46 for(i=l;i<=r;++i)a[i]=temp[i]; 47 CDQ(l,mid),CDQ(mid+1,r); 48 return ; 49 } 50 51 52 int main() 53 { 54 int i,op,x,y,z,xa,xb,ya,yb; 55 56 scanf("%d%d",&s,&w); 57 while(1) 58 { 59 scanf("%d",&op); 60 if(op==1) 61 { 62 scanf("%d%d%d",&x,&y,&z); 63 a[++cnt]=(node){cnt,x,y,0,z,0}; 64 } 65 else if(op==2) 66 { 67 scanf("%d%d%d%d",&xa,&ya,&xb,&yb); 68 a[++cnt]=(node){cnt,xb ,yb ,1,+1,++Ans[0]}; 69 a[++cnt]=(node){cnt,xb ,ya-1,1,-1,Ans[0]}; 70 a[++cnt]=(node){cnt,xa-1,yb ,1,-1,Ans[0]}; 71 a[++cnt]=(node){cnt,xa-1,ya-1,1,+1,Ans[0]}; 72 } 73 else break; 74 } 75 76 sort(a+1,a+cnt+1); 77 78 CDQ(1,cnt); 79 80 for(i=1;i<=Ans[0];++i) 81 printf("%d ",Ans[i]); 82 83 return 0; 84 }