思路
这道题是扫描线线段树的板子题,具体的处理方法:
(1)每个矩形的上下边加入扫描线并标记,下边标记为1,上边标记为-1
(2)让扫描线从从低到高排序,而矩形的左右边从小到大排序
(3)考虑数据范围,我们进行离散化处理
(3)建立线段树,那么区间可表示为区域的y1,y2,以及长度。
(4)遍历所有相邻左右边,每次通过线段树询问出高度,然后面积相加。
注意我们这里要维护的是线段长度而非点的值,所以我们要对线段树进行修改,即左孩子的右值=右孩子的左值,这样才能使维护不出现缝隙。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
//#define int long long
using namespace std;
const int maxn=205;
typedef long long ll;
struct L{
double x,y1,y2,flag;
L(double X=0,double Y1=0,double Y2=0,double T=0){
x=X,y1=Y1,y2=Y2,flag=T;
}
}line[maxn];
struct T{
int l,r; //左右区间
double len; //长度
int lazy;//标记
}t[maxn<<2];
bool cmp(L a,L b){
return a.x<b.x;
}
int n,idx,ans,cnt;
double x1,x2,y1,y2;
double seg[maxn]; //用于存放扫描线
map<double,int>mp; //用于离散化
void build(int p,int l,int r){ //建树
t[p].l=l;t[p].r=r;t[p].len=0;t[p].lazy=0;
if(l==r) return;
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void update(int p){ //更新长度
if(t[p].lazy) t[p].len=seg[t[p].r+1]-seg[t[p].l];
else if(t[p].l==t[p].r) t[p].len=0;
else t[p].len=t[p<<1].len+t[p<<1|1].len;
}
void change(int p,int l,int r,int k){
if(l<=t[p].l&&t[p].r<=r){ //扫描线的y就在区间中
t[p].lazy+=k; //打上标签
update(p); //更新区域高度
return;
}
int mid=t[p].l+t[p].r>>1;
if(l<=mid) change(p<<1,l,r,k);
if(r>mid) change(p<<1|1,l,r,k);
update(p);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
while(n){
cnt=0; //清空线段
for(int i=1;i<=n;i++){
cin>>x1>>y1>>x2>>y2;
line[++cnt]=L(x1,y1,y2,1); //记录竖线
seg[cnt]=y1; //记录扫描线
line[++cnt]=L(x2,y1,y2,-1);
seg[cnt]=y2;
}
sort(line+1,line+1+cnt,cmp); //排序
sort(seg+1,seg+1+cnt);
int m=unique(seg+1,seg+1+cnt)-(seg+1); //去重后有m段扫描线
for(int i=1;i<=m;i++) mp[seg[i]]=i;
build(1,1,m); //建树
double ans=0;
for(int i=1;i<cnt;i++){ //遍历每一条竖线
int L=mp[line[i].y1],R=mp[line[i].y2]-1;//区域的上下边
change(1,L,R,line[i].flag); //查询并更新区域的高
ans+=t[1].len*(line[i+1].x-line[i].x); //累加区域面积
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",++idx,ans);
cin>>n;
}
return 0;
} 
京公网安备 11010502036488号