zoukankan      html  css  js  c++  java
  • [POJ] 3277 .City Horizon(离散+线段树)

    来自这两篇博客的总结

    http://blog.csdn.net/SunnyYoona/article/details/43938355

    http://m.blog.csdn.net/blog/mr_zys/10226425

    题目大意

    如图所示,在一条水平线上有N个建筑物,建筑物都是长方形的,且可以互相遮盖。给出每个建筑物的左右坐标值Ai,Bi以及每个建筑物的高Hi,需要计算出这些建筑物总共覆盖的面积。

    题目数据范围: 
    建筑物个数N:1 <= N <= 40000 
    建筑物左右坐标值Ai, Bi:1 <= Ai,Bi <= 10^9 
    建筑物的高度Hi:1 <= Hi <= 10^9

    这里写图片描述

    分析

    由题意可以知道,这道题需要求的即是这些矩形的面积并。考虑到题目中一个特殊的条件,所有的矩形的一边在一条直线上,我们可以好好利用这个条件:由于所有的矩形在这条直线上的投影均与矩形的一个边长相等。所以,我们可以把矩形“压缩”成直线上的线段,且每条线段都有一个权值,这个权值就是矩形的高度Hi。那么,我们就可以利用线段树进行处理,计算面积并就相当于计算带权的线段并,即S = H * (B – A)。当某条线段被多次覆盖时(比如图中的线段A2B1),只取H值最大的进行计算。如图2.1中的矩形面积并为:S = H1*(B1 – A1) + H2 * (A3 – B2) + H3 * (B3 – A3) 基本思路清楚了,我们现在来考虑具体实现。

    由于题目中矩形的左右坐标的范围非常大(1 <= Ai,Bi <= 10^9),如果建立大小为[1, 10^9)的线段树则会占用大量的空间。我们采用一种离散化的思想来处理这个问题,这种思路在线段树的题目中也是经常会用到的。考虑到一共只有N <= 40000个矩形,那么,这些矩形一共也只有2 * 40000 = 80000个左右坐标值。我们首先将这80000个坐标值按大小排序,对排序后的坐标依次赋予一个新坐标值k(1 <= k <= 80000),这样我们就把长度为[1, 10^9)的线段离散化成[1,80000)的线段了,而最后计算结果时,只需要按照新坐标值找回原始坐标值并代入计算即可。

    举一个简单的例子,假设现在有三条线段[20,60),[10,50),[5,55)。我们将这三条线段的左右端点进行排序,其结果为5,10,20,50,55,60。我们将它们依次赋上新值1,2,3,4,5, 6。这样原始的三条线段被离散化为[3,6),[2,4),[1,5),我们就可以在[1,6)的空间内对其进行处理了。这就是离散化的威力。

    回到原问题上来,当矩形所投影的线段被离散化以后,我们就可以建立线段树了。与之前讲过的初始化略有不同,现在每个线段树的节点不只是记录其所代表的线段是否被覆盖,而且要记录被覆盖的线段的权值。每次加入一个矩形就是在线段树上插入一条带权的线段,插入的实现过程与之前的也有不同。如果当前线段完全覆盖了节点所代表的线段,那么比较这两个线段的权值大小。如果节点所代表的线段的权值小或者在之前根本未被覆盖,则将其权值更新为当前线段的权值。

     1 #include <stdio.h>
     2 #include <algorithm>
     3 using namespace std;
     4 
     5 #define maxn 40400
     6 int max_h[maxn*10];
     7 int line[maxn<<2];
     8 struct buildings
     9 {
    10     int a;
    11     int b;
    12     int h;
    13 } B[maxn];
    14 void build(int l,int r,int rt)
    15 {
    16     max_h[rt] = 0;
    17     if(l + 1== r) return;
    18     int mid = (l + r) >> 1;
    19     build(l,mid,rt<<1);
    20     build(mid,r,rt<<1|1);
    21 }
    22 void update(int l,int r,int rt,int L,int R,int h)
    23 {
    24     if(line[l] == L && line[r] == R) //若插入的线段完全覆盖当前节点所表示的线段
    25     {
    26         max_h[rt] = max(max_h[rt],h);
    27         return ;
    28     }
    29     int m = (l + r) >> 1;
    30     int mid = line[m];
    31     if(R <= mid) update(l,m,rt<<1,L,R,h);// 当前节点的左子节点所代表的线段包含插入的线段
    32     else if(L >= mid) update(m,r,rt<<1|1,L,R,h); // 当前节点的右子节点所代表的线段包含插入的线段
    33     else  // 插入的线段跨越了当前节点所代表线段的中点
    34     {
    35         update(l,m,rt<<1,L,mid,h);
    36         update(m,r,rt<<1|1,mid,R,h);
    37     }
    38 }
    39 long long solve(int l,int r,int rt,int h)
    40 {
    41     //如果rt结点的值大,那么rt的子孙结点都是该值
    42     if( h > max_h[rt]) max_h[rt] = h;
    43     if(l + 1 == r) return (long long) (line[r] - line[l]) * max_h[rt];
    44     int mid = (l + r) >> 1;
    45     return solve(l,mid,rt<<1,max_h[rt]) + solve(mid,r,rt<<1|1,max_h[rt]);
    46 }
    47 int main()
    48 {
    49     int n,cnt;
    50     scanf("%d",&n);
    51     cnt = 0;
    52     for(int i = 0; i < n; i++)
    53     {
    54         scanf("%d%d%d",&B[i].a,&B[i].b,&B[i].h);
    55         line[++cnt] = B[i].a;
    56         line[++cnt] = B[i].b;
    57     }
    58     sort(line+1,line+1+cnt);
    59     cnt = unique(line+1,line+1+cnt) - line -1;//离散处理
    60     build(1,cnt,1);
    61     for(int i = 0; i < n; i++) update(1,cnt,1,B[i].a,B[i].b,B[i].h);
    62     printf("%lld
    ",solve(1,cnt,1,0));
    63     return 0;
    64 }
  • 相关阅读:
    关于bool operator() 几种变种实现的整理
    C# 访问修饰符
    C# 运算符
    c# 集合、存储及排序、迭代
    【并查集】tree
    【最大流】【POJ1149】PIGS
    【拆边最小费用流】【Asia Harbin 2010/2011】【Transportation】
    【DP】【GG选题】
    【DP】【芝麻开门】
    【搜索】【2012 Dhaka Regional】E Poker End Games
  • 原文地址:https://www.cnblogs.com/tsw123/p/4432813.html
Copyright © 2011-2022 走看看