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

参考博客:https://blog.youkuaiyun.com/i1020/article/details/78128568

http://www.cnblogs.com/scau20110726/archive/2013/04/14/3020998.html

求面积并的时候线段树节点中有一个专门表示该节点是否被完全覆盖的变量,这里叫做cnt。如果cnt=0,说明没有完全被覆盖(不代表没有被覆盖),如果cnt=1,表示完全被覆盖,覆盖长度就是区间长度;如果cnt>1,说明也是完全覆盖,但是覆盖了不止一次,覆盖长度和cnt=1的求法是一样的。

 

这道题让我们求覆盖两次及以上的部分面积,那可以在线段树节点中增加一个变量ss,其中s表示该区间内被覆盖了一次及以上的长度,ss表示被覆盖了两次及以上的长度,求面积的方法和面积并一样。

求ss的方法,分情况:

1.cnt>1 : 说明该区间被覆盖两次或以上,那么长度就可以直接计算,就是该区间的长度

剩下的情况就是cnt=1或cnt=0

2.先看叶子节点,因为是叶子没有孩子了,所以被覆盖两次货以上的长度就是0(无论cnt=1或cnt=0都是0,因为是叶子。。。)

3.不是叶子节点 ,且cnt=1.注意这里,cnt=1确切的意义是什么,应该是,可以确定,这个区间被完全覆盖了1次,而有没有被完全覆盖两次或以上则不知道无法确定,那么怎么怎么办了,只要加上t[lch].s + t[rch].s  即,看看左右孩子区间被覆盖了一次或以上的长度,那么叠加在双亲上就是双亲被覆盖两次或以上的长度

3.不是叶子节点,且cnt=0,确切的意义应该是不完全不知道被覆盖的情况(不知道有没有被覆盖,被覆盖了几次,长度是多少都不知道),这种情况,只能由其左右孩子的信息所得

t[lch].ss + t[rch].ss  , 即直接将左右孩子给覆盖了两次或以上的长度加起来,这样才能做到不重不漏

 

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=1010;
double x[maxn<<3];
struct edge
{
    double l,r,h;//一条边的左右端点及高度
    int f;//上边还是下边
}e[maxn<<3];
bool cmp(edge a,edge b)
{
    return a.h<b.h;
}
struct node
{
    int l,r,cnt;//节点的左右端点,以及这个区间被覆盖的程度
    double s,ss;//s表示覆盖了一次及以上的长度,ss表示覆盖了两次及以上的长度
}p[maxn<<3];
void build(int l,int r,int rt)
{
    p[rt].l=l;p[rt].r=r;
    p[rt].cnt=p[rt].s=p[rt].ss=0;
    if(l==r)
        return ;
    int m=(p[rt].l+p[rt].r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}
void pushup(int rt)
{
    if(p[rt].cnt)
        p[rt].s=x[p[rt].r+1]-x[p[rt].l];
    else if(p[rt].l==p[rt].r)
        p[rt].s=0;
    else
        p[rt].s=p[rt<<1].s+p[rt<<1|1].s;

    if(p[rt].cnt>1)
        p[rt].ss=x[p[rt].r+1]-x[p[rt].l];
    else if(p[rt].l==p[rt].r)
        p[rt].ss=0;
    else if(p[rt].cnt==1)
        p[rt].ss=p[rt<<1].s+p[rt<<1|1].s;
    else
        p[rt].ss=p[rt<<1].ss+p[rt<<1|1].ss;
}
void update(int C,int l,int r,int rt)
{
    if(p[rt].l==l && p[rt].r==r)
    {
        p[rt].cnt+=C;
        pushup(rt);
        return ;
    }
    int m=(p[rt].l+p[rt].r)>>1;
    if(r<=m)
        update(C,l,r,rt<<1);
    else if(l>m)
        update(C,l,r,rt<<1|1);
    else
    {
        update(C,l,m,rt<<1);
        update(C,m+1,r,rt<<1|1);
    }
    pushup(rt);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        double x1,y1,x2,y2;
        int cnt=0;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            x[cnt]=x1;x[cnt+1]=x2;
            e[cnt].h=y1;e[cnt+1].h=y2;
            e[cnt].f=1;e[cnt+1].f=-1;
            e[cnt].l=x1=e[cnt+1].l=x1;
            e[cnt].r=x2=e[cnt+1].r=x2;
            cnt+=2;
        }
        int tol=cnt;//把总条数保存下来
        sort(x,x+tol);
        sort(e,e+tol,cmp);
        tol=unique(x,x+tol)-x;//离散化处理
        build(0,tol-1,1);
        double ans=0;
        for(int i=0;i<cnt-1;i++)
        {
            int l=lower_bound(x,x+tol,e[i].l)-x;
            int r=lower_bound(x,x+tol,e[i].r)-x-1;
            update(e[i].f,l,r,1);
            ans+=(e[i+1].h-e[i].h)*p[1].ss;
        }
        printf("%.2lf\n",ans);//保留两位小数
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值