题目链接
【模板】扫描线
题目大意:在平面直角坐标系中求所给矩形面积的并
思路
一个矩形的面积=长*宽。
对于题目中所给的不规则图形,考虑把它们分成若干小矩形来计算面积。
如图,把两个相交的矩形用四条线分为三块,分别求他们的面积得到答案。
如何计算一个小矩形的面积
首先记录下所有平行于轴的边(平行于x轴也可以,这取决于扫描的方向)。
当扫描到一条边时,就可以计算扫过矩形的面积。维护当前这个矩形在延轴方向上被矩形覆盖的长度(也就是图中竖边的总长)。 记当前这条竖边与上一条竖边的横坐标之差为。
。
原理就是为竖边长度,为横边长度。
显然容易得到,可以在输入的时候维护一下所有边的横坐标pos,以为关键字排序后保证从左向右扫描。
该题的难度就在于动态维护。
如何维护
对的维护,即区间修改(也就是每扫描到一个新边(进入一个新的矩形)都要维护该边所在区间的答案)与区间查询(查询当前扫描到的矩形延y轴的长度)。
总的来说,就是要用线段树来维护线段(而非点)。
红字表示线段树的区间。分别为第1~4条线段。
struct Tree{
int fg,len;
}t[4*N];
一个线段树的结点记录当前线段是否被完全覆盖()和这一段上被矩形覆盖的长度()。
假设有一条线,从轴向右扫描,每当扫到一个新的边,有如下两种情况:
- 扫到了一个矩形的左边(将要进入一个新的矩形)。
- 扫到了一个矩形的右边(将要退出当前的矩形)。
每次扫到一个矩形的左边,就让对应线段的,扫到矩形的右边,。易证明。当为时,当前区间没有被完全覆盖。否则就被完全覆盖。
如样例中第一个扫描到的竖边为,上下端点分别为。由上图可知,实际维护的线段是(100~199),则对 (100~199)这个区间进行维护。
继续扫描,扫到新边,则。
这样扫描下去就可以获得最终答案。
对于一个结点p的修改,有如下两种情况:
- 被完全覆盖(),也就是说当前的线段被完全包含于新扫描到的边,只需要用当前线段的右端点减去左端点即可。但是要记得,右端点是当前区间最右边线段的序号+1 这一点非常关键。
- 没有被完全覆盖(),那么它的len就来自左右两个儿子,有。
因为数据范围过大,需要对数据离散化。
代码如下
#include<iostream>
#include<algorithm>
#include<cstdio>
#define lc p<<1
#define rc p<<1|1
typedef long long ll;
using namespace std;
const int N=((1E5+10)*2);
ll n,raw[N],len[4*N],t[4*N];
ll ans;
inline ll read()
{
ll ret=0;char ch=getchar();
while(ch<'0' || ch>'9')ch=getchar();
while(ch>='0' && ch<='9')ret=ret*10+ch-'0',ch=getchar();
return ret;
}
struct LINE{
ll st,ed,hi,ud; //ud -1:上边 1:下边
}l[N];
void push_up(int p,int l,int r)
{
if(t[p])len[p]=raw[r+1]-raw[l];
else if(l==r)len[p]=0;
else len[p]=len[lc]+len[rc];
}
void add(int p,int l,int r,int al,int ar,int val)
{
if(al<=l && ar>=r)
{
t[p]+=val;
push_up(p,l,r);
return;
}
ll mid=(l+r)>>1;
if(al<=mid)add(lc,l,mid,al,ar,val);
if(ar>=mid+1)add(rc,mid+1,r,al,ar,val);
push_up(p,l,r);
}
bool cmp(const LINE &a,const LINE &b)
{
return a.hi<b.hi;
}
int main()
{
// freopen("d.in","r",stdin);
n=read();
for(int i=1,x_,x__,y_,y__;i<=n;++i)
{
x_=read();y_=read();x__=read();y__=read();
l[i]=(LINE){x_,x__,y_,1};
l[i+n]=(LINE){x_,x__,y__,-1};
raw[i]=x_;
raw[i+n]=x__;
}
n<<=1;
sort(raw+1,raw+n+1);
ll nn=unique(raw+1,raw+n+1)-(raw+1);
sort(l+1,l+n+1,cmp);
for(int i=1;i<n;++i)
{
// cout<<l[i].st<<" "<<l[i].ed<<" ";
ll lf=lower_bound(raw+1,raw+nn+1,l[i].st)-raw;
ll rt=lower_bound(raw+1,raw+nn+1,l[i].ed)-raw;
// cout<<lf<<" "<<rt-1<<" "<<l[i].ud<<" ";
if(lf<rt)
add(1,1,nn,lf,rt-1,l[i].ud);
ans+=len[1]*(l[i+1].hi-l[i].hi);
// cout<<len[1]<<endl;
}
cout<<ans<<endl;
}