当前位置: 首页 > news >正文

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];
//struct Point{int x,y,id;}a[N<<1];
//范围 [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;
}
http://www.aitangshan.cn/news/356.html

相关文章:

  • watch命令
  • About Me
  • wget命令参数
  • 2025.8.10学习日记【PyCharm的入门导览】
  • 电子 Doro 安装步骤
  • ps命令详解
  • 面向对象编程:封装
  • 8 面向对象编程 8.8 接口
  • 2025牛客多校第八场 根号-2进制 个人题解 - CUC
  • vCenter上更新证书后,Citrix Delivery Controller(DDC)提示证书不可用
  • 不定长滑动窗口模板
  • 题解:CF1179D Fedor Runs for President
  • 数论杂记 2025.8.11始
  • 8 面向对象编程 8.5. final 关键字 8.6 抽象类 8.7 抽象类最佳实践-模板设计模式
  • [Atlas200I A2] 安装torch-npu
  • 题解:[Vani有约会] 雨天的尾巴 /【模板】线段树合并
  • 8.11随笔
  • 蒸馏大型语言模型并超越其性能
  • 每日随笔
  • webrtc自定义端口和host
  • 第二十九天
  • 【20250805省选训练】T3-简单树题
  • 让CPU省电的方法
  • IFEO劫持
  • GAS_Aura-Highlight Enemies
  • linux中node环境管理
  • 训练专有大模型的核心路径
  • 什么是 IAT Hook?
  • 学习新工具(覆盖程序员绝大部分需求的工具)(zz)
  • 20250811 之所思 - 人生如梦