hdu 1542 Atlantis (扫描线+离散化线段树求多矩形总面积)

题目链接:哆啦A梦传送门

题意:给n个矩形,求它们的总面积,覆盖的只算一次。每个矩形给出左下点和右上点。

参考博客1:

参考博客2

 

扫描线从下往上扫描,每次求出该扫描线的有效长度,再与两条线之间的高度相乘就是最后的面积。

线段树更新,遇到该边为矩形的下边就加进去,遇到该边为矩形的上边就减掉。

 

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
typedef long long LL;
const int N=1000;

struct node{
    double l,r,h; ///每条线的左右x端点,该线的纵坐标
    int d; ///d=1为下边,d=-1为上边
}a[N];

bool cmp(node X,node Y)
{
    return X.h<Y.h;
}

///存储横坐标,离散化使用
double x[N];

struct Node{
    int tl,tr; 
    int s;///该节点被是否被完全覆盖,s不为0即为完全覆盖
    double len; ///该节点的有效长度
}tree[N];

void build(int root,int l,int r)
{
    tree[root].tl=l;
    tree[root].tr=r;
    tree[root].len=tree[root].s=0;

    if(l==r) return ;
    else{
        int mid=(l+r)>>1;
        build(root*2,l,mid);
        build(root*2+1,mid+1,r);
    }
}

void pushup(int root)
{
    if(tree[root].s){ ///该节点被完全覆盖
        tree[root].len=x[tree[root].tr]-x[tree[root].tl-1];
    }
    else if(tree[root].tl==tree[root].tr){ ///这是一个点而不是一条线段
        tree[root].len=0;
    }
    else {///是一条没有被完全覆盖的区间,合并左右子节点
        tree[root].len=tree[root*2].len+tree[root*2+1].len;
    }
}

void update(int root,int ql,int qr,int val)
{
    int tl=tree[root].tl,tr=tree[root].tr;

    if(ql<=tl&&tr<=qr){ ///更新区间包含该节点区间,说明该节点区间被完全覆盖
        tree[root].s+=val;
        pushup(root);
        return;
    }
    int mid=(tl+tr)>>1;
    if(mid>=ql) update(root*2,ql,qr,val);
    if(mid<qr) update(root*2+1,ql,qr,val);
    pushup(root);

}
int main()
{

    int n,T=0;

    while(scanf("%d",&n)&&n)
    {
        int m=0;
        for(int i=1;i<=n;i++)
        {
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            a[m].l=x1,a[m].r=x2,a[m].h=y1,a[m].d=1;
            x[m++]=x1;

            a[m].l=x1,a[m].r=x2,a[m].h=y2,a[m].d=-1;
            x[m++]=x2;

        }

        sort(x,x+m);
        sort(a,a+m,cmp);

        int len=unique(x,x+m)-x;

//        printf("len=%d\n",len);
//        for(int i=0;i<=len;i++)
//            printf("%d=%f ",i,x[i]);
//        puts("");

        
        build(1,1,len);

        double sum=0;

        for(int i=0;i<m;i++) ///离散化各点,并更新线段树
        {
            ///因为我们的树节点是要记录线段的长度,而不是点的权值
            ///这里我们可以将a[i].l的位置+1,这样在更新的时候我们就可以a[r]-a[l-1]来计算长度
            ///这里其实每个叶子节点都代表相邻x的一小段长度
            int l=lower_bound(x,x+len,a[i].l)-x+1;
            int r=lower_bound(x,x+len,a[i].r)-x;

            update(1,l,r,a[i].d);
            ///tree[1].len表示整个区间的有效长度
            sum+=(a[i+1].h-a[i].h)*tree[1].len;
        }
        printf("Test case #%d\n",++T);
        printf("Total explored area: %.2f\n\n",sum);

    }
    return 0;
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值