线段树、扫描线、离散化求面积并(hdu1542)

题目链接:
https://vjudge.net/problem/HDU-1542
大牛博客链接:http://blog.csdn.net/u013480600/article/details/22548393
讲解的很生动。
分析:
首先我们将矩形的上下边分为上位边(即y坐标大的那条平行于x轴的边),和下位边(y坐标小的平行于x轴的边).然后我们把所有矩形的上下位边按照他们y坐标从小到大排序;
需要把x坐标离散化,这样才能用线段树来维护信息.所谓离散化,就是将元素排序,去重,这样得到一个数组a,这个数组就是建立线段树的关键;
主要知识:
关于线段树:线段树的叶节点([l,r],这里l==r,指的是建立线段树数组中的下标),线段树中叶节点代表的都是某一个数组的下标,其他节点则是多个下标。
建立一个线段树必须要有一个数组,根据其坐标建立线段树。本题中叶节点控制的[l,l]实际上代表的是[ a[l],a[l+1] ].线段树中其他节点控制的区间[L,R],也是指的x坐标轴的第L个区间到第R个区间的范围,也就是X[L]到X[R+1]坐标的范围.
关于离散化:
离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变,本题中离散化就是把矩形端点的x坐标放进一个数组中,去掉重复的坐标。,这就用到了unique函数。
关于unique函数:
unique()函数是一个去重函数,STL中unique的函数 unique的功能是去除相邻的重复元素(只保留一个),还有一个容易忽视的特性是它并不真正把重复的元素删除。之所以说比不真正把重复的元素删除,其实是,该函数把重复的元素一到后面去了,然后依然保存到了原数组中返回值是相互间不相同的个数,注意用这个函数之前需要排序。
代码如下:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
using namespace std;
//扫描线,也就是矩形的和x轴平行的那些边,记录高度和左右端点坐标,和
class Seg
{
   public:
        double x1,x2,h;
        int flag;
    Seg(double a=0,double b=0,double c=0,double d=0):x1(a),x2(b),h(c),flag(d){}
};
bool cmp(Seg p,Seg q)
{
    return p.h<q.h||(p.h==q.h&&p.flag>q.flag);
}
const int maxn=100+5;
Seg seg[maxn<<1];
double cnt[maxn<<3],sum[maxn<<3];//cnt表示当前区间状态,0表示被覆盖,大于零则被扫描,sum代表区间长度
double x[maxn<<1];
//建树,
void build(int l,int r,int rt)
{
    cnt[rt]=sum[rt]=0;
    if(l==r)return;
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}
//注意[l,r]间的长度是x[r+1]-x[l]
void push_up(int l,int r,int rt)
{
    if(cnt[rt])sum[rt]=x[r+1]-x[l];
    else if(l==r)sum[rt]=0;
    else sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
//更新,如果是上位边则加一,下位边减一
void update(int ll,int rr,int c,int l,int r,int rt)
{
    if(ll<=l&&rr>=r)
    {
        cnt[rt]+=c;
        push_up(l,r,rt);
        return ;
    }
    int m=(l+r)>>1;
    if(ll<=m)update(ll,rr,c,l,m,rt<<1);
    if(rr>m)update(ll,rr,c,m+1,r,rt<<1|1);
    push_up(l,r,rt);
}
int main()
{
    int n;
    int kase=0;
    while(scanf("%d",&n)!=EOF&&n)
    {
        int nums=0,numx=0;
        for(int i=0;i<n;i++)
        {
            double a,b,c,d;
            scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
            seg[nums++]=Seg(a,c,b,1);
            seg[nums++]=Seg(a,c,d,-1);
            x[numx++]=a;
            x[numx++]=c;
        }
        build(0,numx-2,1);   
        sort(x,x+numx);
        sort(seg,seg+nums,cmp);   
        numx=unique(x,x+numx)-x;  //离散化
        double res=0;
        //让每一条扫描线扫描
        for(int i=0;i<nums-1;i++)
        {
            int ll=lower_bound(x,x+numx,seg[i].x1)-x;
            int rr=lower_bound(x,x+numx,seg[i].x2)-x;
            update(ll,rr-1,seg[i].flag,0,numx-2,1);
            res+=sum[1]*(seg[i+1].h-seg[i].h);   
        }
        printf("Test case #%d\n",++kase);
        printf("Total explored area: %.2lf\n\n",res);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值