【线段树】City Horizon

题意:给出一堆建筑物的左右端点(a,b),范围很大10^9,建筑物都是长方形的且可以相互覆盖,每个建筑物的高度是hi.求这些建筑物总共覆盖的面积。

思路:这题由于建筑物的个数比较多,4*10^4,所以如果暴力枚举o(n^2)的话,肯定会超时的。所以想到用线段树优化来寻找或者标记哪些建筑物相互覆盖,并且在那些建筑物上取相互覆盖中的高度较高者作为被覆盖部分的新的高度。用线段树来执行更改这个动作的话,时间复杂度是lgn的。又由于每个建筑物的左右端点坐标值可能很大,所以线段树的两端点不可能存那么大,也没必要,因为只有40000条线段。所以采用离散化的技术,即把这些大的端点值离散化成1-40000*2范围内的数来表示。建树也能建了。

离散化是一门技术,好的离散化方法在时间和代码复杂度上都很让人感到舒服满意。我离散化的技术不好,像这种是(a,b)成对出现的离散化,我只会用map来暴力,所以时间复杂度很高,不过编码复杂度到是简单。求大牛指教高效美观的离散化。

还有就是,我平时写线段树喜欢把端点写成[x,x]这种模样,不喜欢写成[x,x+1)这种。但这道题,我用前者写的时候,脑袋老是转不动,又想得越来越复杂。。囧。。因为涉及到离散化前后的映射问题。到时采用后者,在某一段端点被更新掉了的情况也不影响,处理很方便。理解不够深入惹的祸。。。


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<vector>
using namespace std;
typedef long long LL;
const int N = 40000 + 10;
inline char Rstr(){char str[10];scanf("%s",str);return str[0];}
map<LL,int>mp;
int n;
LL a[N],b[N],h[N];
int hash[N*2];
LL area;
vector<LL>v;

struct node
{
    int left,right;
    LL height;
};
node seg_tree[N*8];

void creattree(int l,int r,int rt)
{
    seg_tree[rt].left = l;
    seg_tree[rt].right = r;
    seg_tree[rt].height = 0;
    if(l+1==r) return;
    int mid = (l+r)>>1;
    creattree(l,mid,rt<<1);
    creattree(mid,r,rt<<1|1);
}

void insert(int l,int r,int h,int rt)
{
    if(l==seg_tree[rt].left && r==seg_tree[rt].right)
    {
        if(seg_tree[rt].height<h || !seg_tree[rt].height)
            seg_tree[rt].height = h;
        return;
    }
    int mid = (seg_tree[rt].left + seg_tree[rt].right)>>1;
    if(mid>=r) insert(l,r,h,rt<<1);
    else if(mid<=l) insert(l,r,h,rt<<1|1);
    else
    {
        insert(l,mid,h,rt<<1);
        insert(mid,r,h,rt<<1|1);
    }
}

LL cal(int h,int rt)
{
    if(h>seg_tree[rt].height)
        seg_tree[rt].height = h;
    if(seg_tree[rt].left+1==seg_tree[rt].right)
        return (hash[seg_tree[rt].right]-hash[seg_tree[rt].left])
        *seg_tree[rt].height;
    return cal(seg_tree[rt].height,rt<<1) + cal(seg_tree[rt].height,rt<<1|1);
}

int main()
{
    while(scanf("%d",&n)==1)
    {
        mp.clear();
        v.clear();
        area=0;
        creattree(1,n*2,1);
        int k=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld%lld",&a[i],&b[i],&h[i]);
            v.push_back(a[i]);
            v.push_back(b[i]);
        }
        sort(v.begin(),v.end());
        unique(v.begin(),v.end());
        for(int i=0;i<v.size();i++)
        {
            if(!mp[v[i]])
            {        
                mp[v[i]]=k;
                hash[k++]=v[i];
            }
        }
        for(int i=1;i<=n;i++)
        {
            //printf("%d %d\n",mp[a[i]],mp[b[i]]);
            insert(mp[a[i]],mp[b[i]],h[i],1);
        }
        area = cal(seg_tree[1].height,1);
        cout << area << endl;
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值