前言

这是我的第2篇cnblog博客,详细地解说了扫描线+线段树的工作原理以及代码。

前置知识


  • 离散化
  • 线段树

没有学过前置知识的同学可以先学习一下在回来学习这篇博客。

Atlantis问题


题意:给你\(n\)个矩形(\(n\le 100\)),它们有可能重叠在一起,求它们一共覆盖的面积。

扫描线,顾名思义就是一条线在图上扫描。

话不多说,先看图(可能有亿点丑):

扫描线+线段树详解_算法

这个图中,紫色的线条就是扫描线,将扫描线从下往上扫描每一次计算两个扫描线之间的面积(很好计算,因为是矩形)。

设两条扫描线是\(y=y_1\)和\(y=y_2\),每个矩形的宽我们都知道是\(y_2-y_1\),我们现在即要求矩形的长。

而这时候,我们利用类似差分的思想,将每一个矩形的下面打上\(1\)的标签,上面打上\(-1\)的标签,每次用线段树维护区间和即可。

下面是代码(码风有点丑):

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=220;
struct Line
{
double x1,x2,y;
int flag;
};
struct Node
{
int l,r,flag;
double sum;
}tree[maxn<<2];
vector<Line> line;
vector<double> vx;
Line makeline(double x1,double x2,double y,int flag)
{
Line ret;
ret.x1=x1;
ret.x2=x2;
ret.y=y;
ret.flag=flag;
return ret;
}
bool cmp(Line a,Line b)
{
return a.y<b.y;
}
int get(double x)
{
return lower_bound(vx.begin(),vx.end(),x)-vx.begin()+1;
}
void pushup(int x)
{
if(tree[x].flag>0)
{
tree[x].sum=vx[tree[x].r]-vx[tree[x].l-1];
}
else
{
tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
}
}
void build(int x,int l,int r)
{
if(l>r) return;
tree[x].l=l,tree[x].r=r;
tree[x].flag=tree[x].sum=0;
if(l==r) return;
int mid=(l+r)/2;
build(x*2,l,mid);
build(x*2+1,mid+1,r);
}
void update(int x,int l,int r,int delta)
{
if(l>r) return;
if(tree[x].l>=l&&tree[x].r<=r)
{
tree[x].flag+=delta;
pushup(x);
return;
}
int mid=(tree[x].l+tree[x].r)/2;
if(l<=mid)
{
update(x*2,l,r,delta);
}
if(r>mid)
{
update(x*2+1,l,r,delta);
}
pushup(x);
}
int n;
int main()
{
int t=0;
while(scanf("%d",&n))
{
if(!n) break;
++t;
line.clear();
vx.clear();
for(int i=1;i<=n;i++)
{
double x1,x2,y1,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line.push_back(makeline(x1,x2,y1,1));
line.push_back(makeline(x1,x2,y2,-1));
vx.push_back(x1);
vx.push_back(x2);
}
sort(vx.begin(),vx.end());
vx.resize(unique(vx.begin(),vx.end())-vx.begin());
sort(line.begin(),line.end(),cmp);
build(1,1,(int)vx.size()-1);
update(1,get(line[0].x1),get(line[0].x2)-1,line[0].flag);
double ans=0;
for(int i=1;i<(int)line.size();i++)
{
ans+=(double)tree[1].sum*(line[i].y-line[i-1].y);
update(1,get(line[i].x1),get(line[i].x2)-1,line[i].flag);
}
printf("Test case #%d\nTotal explored area: %0.2f\n\n",t,ans);
}
return 0;
}