P5873 [SEERC 2018] Points and Rectangles 解题报告
题解:P5873 [SEERC 2018] Points and Rectangles
简要题意
给定 \(q\) 次操作,每次操作都会往平面内加入一个点或矩形,在每次操作后询问有多少点-矩形对,满足矩形覆盖点(在矩形边上也视作覆盖)。
数据范围:\(q \le 10^5\)
分析
典型的三维偏序问题。看到空间不大,因此放弃树套树,考虑 CDQ 分治。
因为每一次操作后都需要给出答案,因此考虑每一次操作后答案的增加量:
-
对于加点操作,操作后答案的增加量为这个点被在之前加入的且覆盖了该点的矩形数。对于一个点 \((x,y)\),我们要找一个左上角和右下角分别为 \((x_1,y_1)(x_2,y_2)\) 的矩形,满足 \(x_1 \le x \le x_2\) 且 \(y_1 \le y \le y_2\)。
-
对于加矩形操作,操作后答案的增加量为在之前加入的且被该矩形覆盖的点数。形式化描述和上文类似。
接下来考虑怎么刻画这个条件。第一维(时间维度)就是输入顺序,不需要额外处理;第三维的区间限制可以通过数据结构搞定,也不需要太费脑子;主要是第二维怎么刻画区间的这个限制。如果把它换成单点操作,那我们都会做,因此考虑差分。
具体地,对于一个矩形,我们可以把它拆成两条线段并记录该线段统计到的答案的正负性,然后按正常 CDQ 分治做即可。实现时,矩形在询问和插入时的拆分方案有细微差别,详见代码。
最后答案就是增加量的前缀和。
代码
const int N=4e5+100;
int n,m,leny,lenx,c1[N<<1],c[N<<1];
ll val[N];
bool type[N];
//范围 [L,n],[x,y]
struct Node{int opt,x,y,l,f,id;}q[N],q1[N];
int rk[N<<3];
void init(){
For(i,1,n) rk[(i<<1)-1]=q[i].x,rk[(i<<1)]=q[i].y;
sort(rk+1,rk+(n<<1)+1,less<int>());
leny=unique(rk+1,rk+(n<<1)+1)-rk-1;
For(i,1,n){
q[i].x=lower_bound(rk+1,rk+leny+1,q[i].x)-rk;
q[i].y=lower_bound(rk+1,rk+leny+1,q[i].y)-rk;
}
For(i,1,n) rk[i]=q[i].l;
sort(rk+1,rk+n+1,less<int>());
lenx=unique(rk+1,rk+n+1)-rk-1;
For(i,1,n) q[i].l=lower_bound(rk+1,rk+lenx+1,q[i].l)-rk;
}
void add(int x,int val){
for(;x<=leny;x+=lowbit(x))
c[x]+=val;
}
int ask(int x){
int res=0;
for(;x;x-=lowbit(x))
res+=c[x];
return res;
}
void add1(int x,int val){
for(;x<=leny;x+=lowbit(x))
c1[x]+=val;
}
int ask1(int x){
int res=0;
for(;x;x-=lowbit(x))
res+=c1[x];
return res;
}
void solve(int l,int r){
if(l==r) return;
int mid=l+r>>1;
solve(l,mid);
solve(mid+1,r);
int ind=l,ind1=l;
For(i,mid+1,r){
while(ind<=mid && q[ind].l<=q[i].l){
if(q[ind].f==2 || q[ind].f==-2){
add(q[ind].x,q[ind].f/2);
add(q[ind].y+1,-q[ind].f/2);
}
else if(!q[ind].f)
add1(q[ind].x,1);
q1[ind1]=q[ind];
++ind,++ind1;
}
if(q[i].f==1 || q[i].f==-1)
val[q[i].id]+=(ask1(q[i].y)-ask1(q[i].x-1))*q[i].f;
else if(!q[i].f)
val[q[i].id]+=ask(q[i].y);
q1[ind1]=q[i];
++ind1;
}
For(i,l,ind-1)
if(q[i].f==2 || q[i].f==-2){
add(q[i].x,-q[i].f/2);
add(q[i].y+1,q[i].f/2);
}
else if(!q[i].f)
add1(q[i].x,-1);
while(ind<=mid) q1[ind1]=q[ind],++ind,++ind1;
For(i,l,r) q[i]=q1[i];
}
int main()
{
m=read();
int opt,x1,x2,y1,y2;
For(i,1,m){
opt=read();
if(opt==1){
x1=read(),y1=read();
q[++n]=(Node){opt,y1,y1,x1,0,i};
}
else{
x1=read(),y1=read(),x2=read(),y2=read();
//这里是询问的拆法
q[++n]=(Node){opt,y1,y2,x2,1,i};
q[++n]=(Node){opt,y1,y2,x1-1,-1,i};
//这里是插入的拆法
q[++n]=(Node){opt,y1,y2,x1,2,i};
q[++n]=(Node){opt,y1,y2,x2+1,-2,i};
}
}
init();
solve(1,n);
For(i,1,m) val[i]+=val[i-1],printf("%lld\n",val[i]);
return 0;
}