您的位置:首页 > 其它

HDU 1255 覆盖的面积 线段树+扫描线

2015-11-13 16:22 225 查看
覆盖的面积Time Limit:5000MS    Memory Limit:32768KB    64bit IO Format:%I64d
& %I64u
SubmitStatus

Description

给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.



 

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

一道线段树的经典题目。
要解决这个问题,必须要知道如何用线段树求矩形面积并,可以在我的这篇博客中看一下,
当然也可以自己搜索啦。http://blog.csdn.net/wr132/article/details/49797279

那么,当我们已经知道了如何求矩形面积并,问题已经解决了一半,因为求矩形面积并的
问题可以抽象为 求被覆盖了一次及一次以上的区域的面积;而我们的问题是
求被覆盖了两次及两次以上的区域的面积
这样大家就可一看出两道题目的共通之处了,接下来要解决浮点数的问题,我们在计算
矩形面积的时候,需要计算出x轴区域内被覆盖了两次及两次以上的区域的长度,并且这个
长度随着边的加入和删除是要实时更新的,而更新需要使用到线段树,关键是线段树的区间
得是整数,那么我们就需要对x轴做离散化,将浮点的坐标点映射为连续整数坐标点,这里
要注意一下坐标点的含义,它并不表示一个单纯的点,而是一个从该店开始,到下一个点
的一段区间,因为这个连续的区间是不可再分的,因此我们称其元区间,这些区间处于线段树
中的叶子的位置。
下面我再来看线段树。首先,其中的变量有l,r即当前节点的左右区间端点,以及
cnt,这是一个很重要的变量,因为我们是以所有矩形的边为基础进行离散化的,因此在输入
边的时候,就会出现这条边的一部分恰好完全覆盖这个区间的情况,这时候cnt就会相应
的增加或者减少,也就是说,cnt含义是恰好覆盖这个区间的边的数量。还有一个数组
len[2],len[0]表示该区间中覆盖次数大于0次的区域的长度,len[1]表示该区域中覆盖
次数大于1次的区域的长度,这是我们计算的关键,因为我们最终要获得的就是根节点的
len[1],即整个区间中覆盖次数大于1次的区域的长度。那么len到底怎么计算呢?
对于len[0]
1.if当前节点的cnt大于0,那么这个区间内必定包含至少一条完全覆盖区间的边,
因此tr[rt].len[0]=pos[tr[rt].r+1]-pos[tr[rt].l],这里注意,为什么
要把右区间加1,还记得我们说叶子节点也表示一个区间,是从这个点开始到下一个点,也就是
说区间端点是左闭右开的,最右边的点是不包含的,那是因为区间数要比点数少1。
2.else if当前节点是叶子节点,那么这个节点的len[0]肯定是0,因为没有比它
更短的区间了。
3.else,不知道当前节点的覆盖状态,只能由其两个子区间的len[0]值的和来决定。
对于len[1]
1.if当前节点的cnt大于1,那么这个区间内必定包含至少两条完全覆盖区间的边,
因此len[1]可以直接就等于这个区间的长度
2.else if当前节点是叶子节点,那么其len[1]必定为0,因为没有比它更小的区间
可以帮它覆盖两次以上。
3.else if当前节点的cnt==1,那么我们可以确定一定有一条边完全覆盖了这个区间,
那么只要存在一条任意长度的边出现在这个区间内,就可以和那条长边构成覆盖两次及以上,
因此我们只需要去看看左右孩子的len[0],把他们相加就是该节点的len[1]。
4.else 当前节点的cnt==0,也就是说这个大区域的覆盖情况未知,那没办法,只能缩小
面积,查看子区间的len[1],将其相加即可得到这个大区间的len[1]。

当以上的工作都准备完成后,我们就可以计算面积了,方法很简单,每一次向线段树中插入一条边,
更新线段树,并且由根节点的len[1]值和相邻两条线段的高度差计算出一个小矩形的面积,
将面积累加,不要忘了最后保留两位小数。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#define maxn 1010
#define lson(rt) rt<<1
#define rson(rt) rt<<1|1
using namespace std;

struct segment//保存没条线段的信息
{
double l,r,h;
int v;
};

struct node//保存线段树中的节点
{
int l,r;
int cnt;//表示与这个区间完全吻合的线段的个数
double len[2];
};
double pos[maxn<<1];
segment s[maxn<<1];
node tr[maxn<<3];

bool cmp(const segment &a,const segment &b)
{
return a.h<b.h;
}

void push_up(int rt)
{
//更新覆盖1次及1次以上的区域的长度
if(tr[rt].cnt)
tr[rt].len[0]=pos[tr[rt].r+1]-pos[tr[rt].l];
else if(tr[rt].l==tr[rt].r)
tr[rt].len[0]=0;
else
tr[rt].len[0]=tr[lson(rt)].len[0]+tr[rson(rt)].len[0];
//更新覆盖2次及2次以上区域的长度
if(tr[rt].cnt>1)
tr[rt].len[1]=pos[tr[rt].r+1]-pos[tr[rt].l];
else if(tr[rt].l==tr[rt].r)
tr[rt].len[1]=0;
else if(tr[rt].cnt==1)
tr[rt].len[1]=tr[lson(rt)].len[0]+tr[rson(rt)].len[0];
else
tr[rt].len[1]=tr[lson(rt)].len[1]+tr[rson(rt)].len[1];
}

void build(int l,int r,int rt)
{
tr[rt].l=l;tr[rt].r=r;
tr[rt].cnt=tr[rt].len[0]=tr[rt].len[1]=0;
if(l==r)
return;
int m=(tr[rt].l+tr[rt].r)>>1;
build(l,m,lson(rt));
build(m+1,r,rson(rt));
}

int bin_search(double val,int l,int r)//二分查找
{
int m;

while(l<=r)
{
m=(l+r)>>1;
if(pos[m]==val)
{
//printf("m=%d\n",m);
return m;
}
if(val<=pos[m])
r=m;
else
l=m+1;
}
return -1;
}

void update(int l,int r,int v,int rt)
{
if(tr[rt].l==l&&tr[rt].r==r)
{
tr[rt].cnt+=v;
push_up(rt);
return;
}
int m=(tr[rt].l+tr[rt].r)>>1;
if(r<=m)
update(l,r,v,lson(rt));
else if(l>m)
update(l,r,v,rson(rt));
else
{
update(l,m,v,lson(rt));
update(m+1,r,v,rson(rt));
}
push_up(rt);
}

int main()
{
int t,n,p,i,l,r;
double x1,y1,x2,y2;
double res;
scanf("%d",&t);
while(t--)
{
res=0;
p=0;
scanf("%d",&n);
while(n--)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
pos[p]=x1;
pos[p+1]=x2;
s[p].l=s[p+1].l=x1;
s[p].r=s[p+1].r=x2;
s[p].h=y1;
s[p+1].h=y2;
s[p].v=1;
s[p+1].v=-1;
p+=2;
}
sort(pos,pos+p);
sort(s,s+p,cmp);
int m=1;
for(i=1;i<p;i++)//去除相同的x值
if(pos[i]!=pos[i-1])
pos[m++]=pos[i];
build(0,m-1,1);
for(i=0;i<p-1;i++)//最后一条边一定是顶边,不需要插入
{
l=bin_search(s[i].l,0,m-1);
r=bin_search(s[i].r,0,m-1)-1;//注意这里的区间端点是前闭后开
//printf("l=%d,r=%d\n",l,r);
update(l,r,s[i].v,1);
res+=tr[1].len[1]*(s[i+1].h-s[i].h);
}
res+=0.004;
printf("%.2f\n",res);
}
return 0;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息