Description
有N个操作。
- 在平面直角坐标系中画一个边与坐标轴平行的矩形,输入左下角点坐标和右上角点坐标。
- 给出一个点坐标,查询被多少矩形覆盖。
所有点均在第一象限或X,Y轴的非负半轴上,N<=200000
Solution
经典的CDQ分治(整体二分)。
设矩形(x,y)(p,q)
一个矩形可以拆成四个点,(x,y),(x,q+1),(p+1,y),(p+1,q+1)
第一个点和第四个点权值为1,另两个为-1。
那么对于查询就是统计原点与这个点构成的矩形中的所有点权和(思考为什么?)
拆点以后,根据读入时间存起来。
显然只有左边的点对右边才有影响
二分X坐标,扫一遍对于
[L,mid]
的操作点就将它Y坐标打进树状数组,对于
[mid,R]
的查询点就用它的Y坐标在树状数组上查询
这样我们相当于将当前所有点分成了两部分,两个数组存起来分治下去,在mid上的点不用存。
因为坐标从0开始,可以先全部+1方便树状数组统计。
复杂度
O(NlogN)
Code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 600015
using namespace std;
int a[2*N][4],n,q,c[N],lim,ans[N],b[20][N][4],n1,le[20];
int lowbit(int k)
{
return k&(-k);
}
void put(int k,int v)
{
while(k<=lim) c[k]+=v,k+=lowbit(k);
}
int get(int k)
{
int s=0;
while(k>0) s+=c[k],k-=lowbit(k);
return s;
}
void turn(int p,int q,int w)
{
le[p]++;
fo(i,0,3) b[p][le[p]][i]=b[q][w][i];
}
void doit(int l,int r,int p)
{
int mid=(l+r)/2;
if(l>r) return;
fo(i,1,le[p])
{
if(b[p][i][2]!=0&&b[p][i][0]<=mid) put(b[p][i][1],b[p][i][2]);
if(b[p][i][2]==0&&b[p][i][0]>=mid) ans[b[p][i][3]]+=get(b[p][i][1]);
}
fo(i,1,le[p]) if(b[p][i][2]!=0&&b[p][i][0]<=mid) put(b[p][i][1],-b[p][i][2]);
if(l<mid)
{
n1++,le[n1]=0;
fo(i,1,le[p]) if(b[p][i][0]<mid) turn(n1,p,i);
doit(l,mid-1,n1);
n1--;
}
if(mid<r)
{
n1++,le[n1]=0;
fo(i,1,le[p]) if(b[p][i][0]>mid) turn(n1,p,i);
doit(mid+1,r,n1);
n1--;
}
}
void nwp(int x,int y,int z,int t)
{
b[1][++le[1]][0]=x;
b[1][le[1]][1]=y;
b[1][le[1]][2]=z;
b[1][le[1]][3]=t;
}
int main()
{
cin>>n;
q=0;
n1=1;
fo(i,1,n)
{
int p,x,y,l=0,r;
scanf("%d",&p);
if(p==0)
{
scanf("%d%d%d%d",&x,&y,&l,&r);
x++,y++,l++,r++;
nwp(x,y,1,i);
nwp(l+1,y,-1,i);
nwp(x,r+1,-1,i);
nwp(l+1,r+1,1,i);
}
else scanf("%d%d",&x,&y),nwp(++x,++y,0,i);
lim=max(lim,max(x,l+1));
}
doit(1,lim,1);
fo(i,1,le[1])
{
if(b[1][i][2]==0) printf("%d\n",ans[b[1][i][3]]);
}
}