题目简意
有n个同学,每个同学有a,b,c三个数(c=a+b)
现在,有m次询问,每次给你三个数A,B,C问你满足a>=A,b>=B,c>=C的同学的人数
一道明显的三维偏序问题,只是空间把主席树卡了(别问我为什么知道qwq)
所以,这里我们就要打空间复杂度更小的cdq分治
我们先考虑下,怎么做题,因为我们肯定不能对每次询问都单独的做一次cdq分治,这样时间复杂度会原地爆炸,所以,我们可以考虑把所有询问存下来,一起做
我们可以把每个询问看做一个学生,这样,我们只需要求出比这些“学生”三个属性都大的学生(不包括询问产生的“学生”)的个数即可
那么, 我们就可以直接上cdq分治。
我们先按a由大到小排序(如果相等,将“学生”放后面,以保证可以询问的时候,符合条件的都添加好了)
然后,我们再在cdq分治中,按b由大到小做归并排序,同时,因为我们只需要计算左边区间对右边区间的贡献,所以,我们按顺序,将左边区间的所有学生(同样不包括询问)加入一个数据结构(我是权值线段树),对于右边区间,我们对所有“学生”进行查询操作即可~
需要注意的是,为了方便进行值的查询,我们最好先将数据进行离散化(反正只需要知道大小关系即可)
代码:
#include<bits/stdc++.h> using namespace std; const int N=2e5+1; int laz[N<<2],W[N<<2]; struct node{ int a,b,c,num; }q[N<<1]; struct lsh{ int x,w; }p[N<<1]; int ans[N]; inline bool kkk(lsh x,lsh y){ return x.w<y.w; } inline bool ttt(node x,node y){ return x.a!=y.a?x.a>y.a:x.num<y.num; } inline void down(int now,int l,int r){ if(laz[now]){ laz[now]=0; if(l==r){ return; } W[now<<1]=W[now<<1|1]=0; laz[now<<1]=laz[now<<1|1]=1; } } inline void insert(int now,int l,int r,int x){ down(now,l,r); ++W[now]; if(l==r){ return; } int mid=(l+r)>>1; if(x<=mid){ insert(now<<1,l,mid,x); }else{ insert(now<<1|1,mid+1,r,x); } } inline int find(int now,int l,int r,int lc,int rc){ down(now,l,r); if(lc<=l&&r<=rc){ return W[now]; } int mid=(l+r)>>1,res=0; if(lc<=mid){ res+=find(now<<1,l,mid,lc,rc); } if(rc>mid){ res+=find(now<<1|1,mid+1,r,lc,rc); } return res; } int n,Q,tot; node a[N]; inline bool ppp(node x,node y){ return x.b!=y.b?x.b>y.b:x.num<y.num; } inline void cdq(int l,int r){ if(l==r){ return; } int mid=(l+r)>>1,al=0; cdq(l,mid),cdq(mid+1,r); //对答案加上左边对右边的贡献即可 //两边已按1.q[i].b,2.q[i].num由大到小排序 W[1]=0,laz[1]=1;//clear int L=l,R=mid+1;//两边指针 while(L<=mid&&R<=r){ if(ppp(q[L],q[R])){ //将q[L]放入 if(!q[L].num){ insert(1,1,n,q[L].c); } a[++al]=q[L++]; }else{ if(q[R].num){ ans[q[R].num]+=find(1,1,n,q[R].c,n); } a[++al]=q[R++]; } } while(L<=mid){ if(!q[L].num){ insert(1,1,n,q[L].c); } a[++al]=q[L++]; } while(R<=r){ if(q[R].num){ ans[q[R].num]+=find(1,1,n,q[R].c,n); } a[++al]=q[R++]; } for(int i=l;i<=r;++i){//排序完毕~ q[i]=a[i-l+1]; } } int main(){ scanf("%d%d",&n,&Q); for(int i=1;i<=n;++i){ scanf("%d%d",&q[i].a,&q[i].b); q[i].c=q[i].a+q[i].b;q[i].num=0; } for(int i=1;i<=Q;++i){ scanf("%d%d%d",&q[n+i].a,&q[n+i].b,&q[n+i].c); q[n+i].num=i; } //离散化 n=n+Q; for(int i=1;i<=n;++i){ p[i].x=i,p[i].w=q[i].a; } sort(p+1,p+n+1,kkk); q[p[1].x].a=tot=1; for(int i=2;i<=n;++i){ if(p[i].w!=p[i-1].w){ ++tot; } q[p[i].x].a=tot; } for(int i=1;i<=n;++i){ p[i].x=i,p[i].w=q[i].b; } sort(p+1,p+n+1,kkk); q[p[1].x].b=tot=1; for(int i=2;i<=n;++i){ if(p[i].w!=p[i-1].w){ ++tot; } q[p[i].x].b=tot; } for(int i=1;i<=n;++i){ p[i].x=i,p[i].w=q[i].c; } sort(p+1,p+n+1,kkk); q[p[1].x].c=tot=1; for(int i=2;i<=n;++i){ if(p[i].w!=p[i-1].w){ ++tot; } q[p[i].x].c=tot; } //三维偏序 sort(q+1,q+n+1,ttt); //先按A由大到小排序 cdq(1,n); for(int i=1;i<=Q;++i){ printf("%d\n",ans[i]); } return 0; }