扫描线


扫描线

P5490 【模板】扫描线

现在我们有一些矩形在平面直角坐标系上:

原图

现在我们要求他们的面积的并(平面直角坐标系被覆盖的面积)

我们可以将每个矩形看成有两条线组成(左边一条为蓝色,右边一条为绿色)

将所有的蓝色线标记成 $+1$,将所有的绿色线标记为 $-1$ ,从左向右扫得到的数值即为当前点被覆盖的次数,又与我们求的是并,所以只记录有无被覆盖过即可(记录被覆盖几次是记录所有矩形的面积)。

将所有线段处理出来后可以用线段树维护,每次维护的是整个 $y$ 轴($1$ 维直线)每次统计两次之间的 $x$ 的差和之间 $y$ 轴上的长度相乘即可。

扫描的过程

开始扫描

记录 $A,B$ 坐标,$y_{[B,A]}$ 更新到线段树上。

扫描-1

统计记录 $(y_A-y_B)\times (x_C-x_A)$(小矩形面积)

记录 $C,D$ 坐标,$y_{[D,C]}$ 更新到线段树上。

扫描-2

统计记录 $(y_C-y_D)\times (x_E-x_D)$(小矩形面积)

记录 $E,F$ 坐标,$y_{[F,E]}$ 更新到线段树上。

扫描-3

统计记录 $((y_C-y_D)+(y_E-y_F))\times (x_H-x_F)$(小矩形面积)

记录 $G,H$ 坐标,$y_{[H,G]}$ 更新到线段树上。

扫描-4

统计记录 $(y_G-y_F)\times (x_I-x_G)$(小矩形面积)

记录 $I,J$ 坐标,$y_{[J,I]}$ 更新到线段树上(值为负,说明有矩形到头了,要更新回去)。

扫描-5

统计记录 $(y_G-y_F)\times (x_K-x_I)$(小矩形面积)

记录 $K,L$ 坐标,$y_{[L,K]}$ 更新到线段树上。

扫描-6

统计记录 $((y_B-y_A)+(y_E-y_F))\times (x_M-x_K)$(小矩形面积)

记录 $M,N$ 坐标,$y_{[N,M]}$ 更新到线段树上。

扫描-7

统计记录 $(y_E-y_F)\times (x_O-x_M)$(小矩形面积)

记录 $O.P$ 坐标,$y_{[P,O]}$ 更新到线段树上。

完成

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
using namespace std;
int r_r(){//快读 
  int k=0,f=1;
  char c=getchar();
  while(!isdigit(c)){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(isdigit(c)){
    k=(k<<1)+(k<<3)+(c^48);
    c=getchar(); 
  }
  return k*f;
}
const int o_o=1e6+10;
struct po{
  long long l,r;//记录线段在 y 轴的左右边界 
  long long x;//记录 x 的坐标 
  long long v;//记录位置(左边界右边界) 
  bool operator<(po b){//x 坐标从小到大排序,x长度相同,长度长的优先
    return x==b.x?l>b.l:x<b.x;
  }
}l[o_o];
struct pp{
  int s_m;//节点价值 
  int l_n;//长度 
  pp(){
    s_m=l_n=0;//初始化 
  }
}t_r[o_o<<2];
long long n,t_t=0,a_s=0,y_y[o_o];
void u_p(int k,int l,int r){
  if(t_r[k].s_m>0)t_r[k].l_n=y_y[r]-y_y[l];//这一段被覆盖,更新长度 
  else t_r[k].l_n=t_r[k<<1].l_n+t_r[k<<1|1].l_n;//更新长度 
}
void a_d(int k,int l,int r,int x,int y,int v){
  if(x>=y_y[r]||y_y[l]>=y) return;//没有重合部分 
  if(x<=y_y[l]&&y_y[r]<=y){
    t_r[k].s_m+=v;//更新值 
    u_p(k,l,r);
    return ; 
  }
  int m_i=(l+r)>>1; 
  a_d(k<<1,l,m_i,x,y,v);//统计左边 
  a_d(k<<1|1,m_i,r,x,y,v);//统计右边 
  u_p(k,l,r);
}
int main(){
  n=r_r();
  for(int i=1,x_1,y_1,x_2,y_2;i<=n;i++){
    x_1=r_r(),y_1=r_r(),x_2=r_r(),y_2=r_r();
    
    //存入矩形基本信息 
    l[(i<<1)-1].x=x_1;
    l[(i<<1)-1].l=y_1;
    l[(i<<1)-1].r=y_2;
    l[(i<<1)-1].v=1;//左边界标记 
    l[i<<1].x=x_2;
    l[i<<1].l=y_1;
    l[i<<1].r=y_2;
    l[i<<1].v=-1;//右边界标记 
    
    //记录线段长度 
    y_y[(i<<1)-1]=y_1;
    y_y[i<<1]=y_2;
  }
  n<<=1;
  sort(y_y+1,y_y+n+1);//将所有的 y 坐标排序 
  sort(l+1,l+n+1);//将所有线段排序 
  for(int i=1;i<=n;i++)if(y_y[i]!=y_y[i+1])y_y[++t_t]=y_y[i];//去重 
  for(int i=1;i<n;i++){
    a_d(1,1,n,l[i].l,l[i].r,l[i].v);//线段的值增加 
    a_s+=(l[i+1].x-l[i].x)*t_r[1].l_n;//统计结果 
  }
  printf("%lld\n",a_s);
  return 0;
}

文章作者: 王大神——A001
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 王大神——A001 !
  目录