POJ1151 Atlantis 线段树+扫描线(离散化)

题目链接:http://poj.org/problem?id=1151


题目大意:给你一些矩形,其顶点坐标是浮点数,可能相互重叠,问这些矩形覆盖到的面积是多少。


分析:我们将Y轴进行离散化,n个矩形的2n条边界,最多可将Y轴分成2n-1个区域,我们对这些区间进行编号,建立线段树。

const int M=210;
struct segment
{
    int l,r;
    double len; //表示区间上有多长的部分是落在矩形中的
    int cov; //该区间当前被多少个矩形完全包含
    int mid()
    {
        return (l+r)>>1;
    }
}tree[M<<2];


将矩形的纵边从左到右排序,然后依次将这些纵边插入线段树,要记住哪些纵边是矩形的左边界(始边),哪些是右边界(终边),以便插入时,对len和cov做不同的修改。插入一条边后,就根据根结点的len值增加总覆盖面积area的值:area+=len*(该边到下一条边的距离)。


实现代码如下:

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int M=210;
struct segment
{
    int l,r;
    double len; //表示区间上有多长的部分是落在矩形中的
    int cov; //该区间当前被多少个矩形完全包含
    int mid()
    {
        return (l+r)>>1;
    }
}tree[M<<2];
struct Line
{
    double x,y1,y2;
    bool bleft; //标记该线段是否为矩形的左边
    bool operator <(const Line &a) const
    {
        return x<a.x;
    }
}line[M];
double y[M]; //记录所有矩形的y坐标
void build(int rt,int l,int r)
{
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].len=0;
    tree[rt].cov=0;
    if(l==r) return ;
    int m=tree[rt].mid();
    build(rt<<1,l,m);
    build(rt<<1|1,m+1,r);
}
void update(int rt,int l,int r,bool op)
{
    if(tree[rt].l==l&&tree[rt].r==r)
    {
        if(op) 
        {//当前边是矩形的左边界的话,在该区间插入矩形左边的一部分或者全部,覆盖区间[l,r]
            tree[rt].len=y[r+1]-y[l];
            tree[rt].cov++;
        }
        else
        {
            tree[rt].cov--;
            if(tree[rt].cov==0)
            {
                if(tree[rt].l==tree[rt].r) tree[rt].len=0;
                else tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
            }
        }
        return ;
    }
    int m=tree[rt].mid();
    if(r<=m) update(rt<<1,l,r,op);
    else if(l>m) update(rt<<1|1,l,r,op);
    else
    {
        update(rt<<1,l,m,op);
        update(rt<<1|1,m+1,r,op);
    }
    if(tree[rt].cov==0) //如果不为0,说明该区间当前仍然被某个矩形完全包含,此时不更新len值
      tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
}
int main()
{
    int n,T=1;
    while(scanf("%d",&n)&&n)
    {
        int yc=0,lc=0; //y[]和line[]的计数器
        for(int i=0;i<n;i++)
        {
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[yc++]=y1; y[yc++]=y2;
            line[lc].x=x1;
            line[lc].y1=y1;
            line[lc].y2=y2;
            line[lc].bleft=true;
            lc++;
            line[lc].x=x2;
            line[lc].y1=y1;
            line[lc].y2=y2;
            line[lc].bleft=false;
            lc++;
        }
        sort(y,y+yc);
        yc=unique(y,y+yc)-y; //排序并去重
        build(1,0,yc); 
        sort(line,line+lc);
        double area=0;
        for(int i=0;i<lc-1;i++) //插入每条边
        {
            int l=lower_bound(y,y+yc,line[i].y1)-y; //二分找出当前边离散化后对应的线段树的区间端点
            int r=lower_bound(y,y+yc,line[i].y2)-y;
            update(1,l,r-1,line[i].bleft);
            area+=tree[1].len*(line[i+1].x-line[i].x);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",T++,area);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值