题意: 简化就是有两种操作,一种是插入(x,y,z)这个坐标,第二种是查询(x1,y1,z1)到(x2,y2,z2)(x1<=x2,y1<=y2,z1<=z2)的长方体包含多少个点。
解析: 将查询分成8个点,离线做,离散化z值,
两次CDQ,第一次归并排x值,第二次归并排y值,z值用bit树维护更新
查询。
代码
#include<cstdio> #include<cstring> #include<algorithm> #include<string> using namespace std; const int maxn=9*50000; int N,Q,ans[maxn]; struct node { int x,y,z,id,s;//(x,y,z)三维坐标,id编号,s代表状态 node(int x=0,int y=0,int z=0,int id=0,int s=0) :x(x),y(y),z(z),id(id),s(s){} }A[maxn],B[maxn],C[maxn];//A保存原数组,B保存中间过程,C用于归并排序临时数组 bool cmpid(const node& a,const node& b){ return a.id<b.id; } //排id bool cmpz(const node& a,const node& b){ return a.z<b.z; } //排z值 int tree[maxn];//bit树实现部分 int lowbit(int x){ return x&(-x); } void Add(int i,int d){ for(;i<maxn;i+=lowbit(i)) tree[i]+=d; } int Sum(int i) { int ret=0; for(;i>0;i-=lowbit(i)) ret+=tree[i]; return ret; } void CDQ2(int l,int r) //归并排y值,对z值查询更新 { if(l>=r) return; int mid=(l+r)/2; CDQ2(l,mid); CDQ2(mid+1,r); int ls=l,rs=mid+1; while(rs<=r) { while(ls<=mid&&B[ls].y<=B[rs].y) //选取y较小的 { if(B[ls].s==0) Add(B[ls].z,1); //如果是0则需要插入bit树 ls++; } if(B[rs].s!=0) ans[B[rs].id]+=Sum(B[rs].z)*B[rs].s; //右边的是查询 rs++; } while(ls>l) //恢复 { ls--; if(B[ls].s==0) Add(B[ls].z,-1); } ls=l,rs=mid+1; for(int i=l;i<=r;i++) //归并排序过程,排y值 { if((ls<=mid&&B[ls].y<=B[rs].y)||rs>r) C[i]=B[ls++]; else C[i]=B[rs++]; } for(int i=l;i<=r;i++) B[i]=C[i]; } void CDQ1(int l,int r)//归并排x值,选取的过程是时序 { if(l==r) return; int mid=(l+r)/2; CDQ1(l,mid); CDQ1(mid+1,r); int ls=l,rs=mid+1,k=0; while(rs<=r) { while(ls<=mid&&A[ls].s!=0) ls++; //前一半而且是查询不管 while(rs<=r&&A[rs].s==0) rs++; //后一半而且是插入不管 if(rs>r) break; //这个地方可能难理解,仔细 想一下如果右边已经没有查询的操作了, //剩下的左边的时序绝对比最后一次加到B中的查询的操作时序要大 if((A[ls].x<=A[rs].x&&ls<=mid)||rs>r) B[k++]=A[ls++]; else B[k++]=A[rs++]; } if(k>0) CDQ2(0,k-1); //再一次CDQ ls=l,rs=mid+1; for(int i=l;i<=r;i++) //归并排序 { if((ls<=mid&&A[ls].x<=A[rs].x)||rs>r) C[i]=A[ls++]; else C[i]=A[rs++]; } for(int i=l;i<=r;i++) A[i]=C[i]; } int main() { int T; scanf("%d",&T); while(T--) { N=0; int type,x1,y1,z1,x2,y2,z2; scanf("%d",&Q); while(Q--) { scanf("%d",&type); if(type==1) { scanf("%d%d%d",&x1,&y1,&z1); A[N++]=node(x1,y1,z1,N,0); //要插入的点 } else { scanf("%d%d%d",&x1,&y1,&z1); scanf("%d%d%d",&x2,&y2,&z2); A[N++]=node(x2,y2,z2,N,1); //分成8个查询的点 A[N++]=node(x2,y1-1,z2,N,-1); A[N++]=node(x1-1,y2,z2,N,-1); A[N++]=node(x2,y2,z1-1,N,-1); A[N++]=node(x1-1,y1-1,z2,N,1); A[N++]=node(x1-1,y2,z1-1,N,1); A[N++]=node(x2,y1-1,z1-1,N,1); A[N++]=node(x1-1,y1-1,z1-1,N,-1); } } memset(ans,0,sizeof(ans)); sort(A,A+N,cmpz); //按照z值排序 int ka=1; A[N].z=-1; for(int i=0;i<N;i++) if(A[i].z!=A[i+1].z) A[i].z=ka++; //离散化 else A[i].z=ka; sort(A,A+N,cmpid); //按照时序排回来 memset(tree,0,sizeof(tree)); CDQ1(0,N-1); sort(A,A+N,cmpid); //再排回来 for(int i=0;i<N;i++) //输出答案 { if(A[i].s==0) continue; int sum=0; for(int j=0;j<8;j++) sum+=ans[i+j]; printf("%d\n",sum); i+=7; } } return 0; }
时间: 2024-11-06 15:59:07