扫描线
面积并
求 \(n\) 个四边平行于坐标轴的矩形的面积并。
思路
容易想到计算每一个长方形的面积,然后去掉重叠的面积就为答案。但是重叠的面积并不好算。
于是我们考虑把图形切割成一块一块的小矩形:(图片均来源于网络)

那我们只需要计算每个小矩形的面积之和就好了,我们假设有一条直线,从下往上依次截取横边:

那我们根据这条扫描线,可以把上图分成3个部分,每个部分宽度可以用两条截取线段高度之差(\(h_{i+1}-h_i\))求得。那我们如何求长呢?
对于每一条横边,\([l_i,r_i]\) 这条部分会被覆盖,最后要求被覆盖的总长度,所以离散化完 \(x\) 坐标后,可以用线段树来实现。
那我们这个线段树需要维护两个信息:
-
是否被完全覆盖
-
被覆盖的总长度
那么算总长度只需要 \(tree[1].sum\) 就好了。如何判断当前横边是否还要继续算呢,只需要给矩形的下边赋值为 \(1\),上边赋值为 \(-1\)。这样就不会多算了。
其他与模板一样,只需要修改信息时略微改动:(注意区间左闭右开或左开右闭)
-
如果该区间被完全覆盖:总和为右端点减左端点。
-
如果该区间未被完全覆盖:总和为左右两儿子之和。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int N=7e6+5;
int n,m,ans;
struct st{
int l,r,c,sum;
}tree[N];
struct node{
int l,r,h,w;
}line[N];
bool cmp(node a,node b){
return a.h<b.h;
}
int x[N];
int ls(int p){return p<<1;}
int rs(int p){return p<<1|1;}
void push_up(int p){
if(tree[p].c){
tree[p].sum=x[tree[p].r+1]-x[tree[p].l];
}
else tree[p].sum=tree[ls(p)].sum+tree[rs(p)].sum;
}
void build(int p,int l,int r){
tree[p].l=l,tree[p].r=r;
if(l==r){
tree[p].c=tree[p].sum=0;
return;
}
int mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
void update(int p,int l,int r,int d){
if(x[tree[p].r+1]<=l||r<=x[tree[p].l]){
return;
}
if(l<=x[tree[p].l]&&x[tree[p].r+1]<=r){
tree[p].c+=d;
push_up(p);
return;
}
int mid=(tree[p].l+tree[p].r)>>1;
update(ls(p),l,r,d);
update(rs(p),l,r,d);
push_up(p);
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
int a,b,c,d;
cin>>a>>b>>c>>d;
line[2*i-1]={a,c,b,1};
line[2*i]={a,c,d,-1};
x[2*i-1]=a;
x[2*i]=c;
}
sort(line+1,line+2*n+1,cmp);
sort(x+1,x+2*n+1);
m=unique(x+1,x+2*n+1)-x-1;
build(1,1,m-1);
for(int i=1;i<2*n;i++){
update(1,line[i].l,line[i].r,line[i].w);
ans+=tree[1].sum*(line[i+1].h-line[i].h);
}
cout<<ans<<"\n";
return 0;
}

浙公网安备 33010602011771号