来自这两篇博客的总结
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 }