覆盖的面积 HDU - 1255 (扫描线+线段树)
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.
注意:本题的输入数据较多,推荐使用scanf读入数据.
Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
Sample Input
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
Sample Output
7.63
0.00
题目大意:
给你n个矩形,让你求,覆盖了两次或两次以上的面积。(中文题,题意清晰)
解题思路:
这是一道扫描线的题目。但是不是太裸的让你直接求面积,而是求覆盖了两次的面积。
这就要求我们要对扫描线模板中的几个参数有更好的认识。
struct Node{
int l,r;
int s;//标记覆盖情况
double len;//覆盖了一次的区间长度
double len2;//覆盖了两次的区间长度
}tree[maxn];
我们在树的结点中增加了一个len2 变量,表示这个区间被覆盖了两次的长度
s还是标记有没有被覆盖,s=1表示被全部覆盖了至少1次,s=2表示被覆盖了至少2次。
如果s>=2,那么我们的len2 就直接是这个区间长度了,这个没啥好说的
和更新len一样,如果这个区间缩成一个点的时候,就是len2=0了
再有就是s=1的时候,这时侯这个区间被覆盖了至少一次,所以只要把他的左右孩子覆盖了一次和本区间的叠加起来就是覆盖了两次。所以这时候len2 = 左.len1+右.len1.
其他情况下,len2就直接由左右子树的结果加和了。
void pushup(int cnt){
//更新len1;
if(tree[cnt].s){
tree[cnt].len=x[tree[cnt].r]-x[tree[cnt].l-1];
}
///叶子节点
else if(tree[cnt].l==tree[cnt].r){
tree[cnt].len=0.0;
}
else{
tree[cnt].len=tree[cnt*2].len+tree[cnt*2+1].len;
}
//更新len2;
if(tree[cnt].s>=2){
tree[cnt].len2=x[tree[cnt].r]-x[tree[cnt].l-1];
}
else if(tree[cnt].l==tree[cnt].r){
tree[cnt].len2=0;
}
else if(tree[cnt].s==1){
tree[cnt].len2=tree[cnt*2].len+tree[cnt*2+1].len;
}
else{
tree[cnt].len2=tree[cnt*2].len2+tree[cnt*2+1].len2;
}
}
注意的是,别光更新len2,len1得更新,就按板子更新就行。
最后的结果就是tree[1].len2*高度差。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn =5e4+10;
double x[maxn];
int n;
///这个结构体存的是扫描线
struct node{
double l,r,h;
int d;//上边是-1 下边是1
}a[maxn];
//存树的节点
struct Node{
int l,r;
int s;//标记覆盖情况
double len;//覆盖了一次的区间长度
double len2;//覆盖了两次的区间长度
}tree[maxn];
void build(int l,int r,int cnt){
tree[cnt].l=l;
tree[cnt].r=r;
tree[cnt].s=0;
tree[cnt].len=0;
tree[cnt].len2=0;
if(l==r) return ;
else{
int mid = (l+r)/2;
build(l,mid,cnt*2);
build(mid+1,r,cnt*2+1);
}
}
void pushup(int cnt){
//更新len1;
if(tree[cnt].s){
tree[cnt].len=x[tree[cnt].r]-x[tree[cnt].l-1];
}
///叶子节点
else if(tree[cnt].l==tree[cnt].r){
tree[cnt].len=0.0;
}
else{
tree[cnt].len=tree[cnt*2].len+tree[cnt*2+1].len;
}
//更新len2;
if(tree[cnt].s>=2){
tree[cnt].len2=x[tree[cnt].r]-x[tree[cnt].l-1];
}
else if(tree[cnt].l==tree[cnt].r){
tree[cnt].len2=0;
}
else if(tree[cnt].s==1){
tree[cnt].len2=tree[cnt*2].len+tree[cnt*2+1].len;
}
else{
tree[cnt].len2=tree[cnt*2].len2+tree[cnt*2+1].len2;
}
}
void updata(int pl,int pr,int flag,int cnt){
int l=tree[cnt].l;
int r=tree[cnt].r;
if(pl<=l && r<=pr){
tree[cnt].s+=flag;
pushup(cnt);
return ;
}
int mid=(l+r)/2;
if(pl<=mid) updata(pl,pr,flag,cnt*2);
if(pr>mid) updata(pl,pr,flag,cnt*2+1);
pushup(cnt);
}
bool cmp(struct node x,struct node y){
return x.h<y.h;
}
int main()
{
int cas=1;
int t;
cin>>t;
while(t--){
cin>>n;
int Cnt=0;
double SUM=0.0;
for(int i=1;i<=n;i++){
double x1,x2,y1,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
if(y1>y2) swap(y1,y2);
SUM=SUM+abs((x2-x1))*abs((y2-y1));
a[Cnt].l=x1,a[Cnt].r=x2,a[Cnt].h=y1,a[Cnt].d=1;
x[Cnt++]=x1;
a[Cnt].l=x1,a[Cnt].r=x2,a[Cnt].h=y2,a[Cnt].d=-1;
x[Cnt++]=x2;
}
sort(x,x+Cnt);
sort(a,a+Cnt,cmp);
int len=unique(x,x+Cnt)-x;//去重
build(1,len,1);
double sum=0.0;
for(int i=0;i<Cnt;i++){
int l=lower_bound(x,x+len,a[i].l)-x+1;
int r=lower_bound(x,x+len,a[i].r)-x;
updata(l,r,a[i].d,1);
sum+=(tree[1].len2*(a[i+1].h-a[i].h));
// cout<<sum<<endl;
}
printf("%.2lf\n",sum);
}
return 0;
}