【HDU】 1255 覆盖的面积
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1255
看这个题目AC的人数也该知道,是线段树求面积的经典题,既然是经典题,岂有不AC之理?
推荐:http://www.cnblogs.com/ka200812/archive/2011/11/13/2247064.html

说是变态,主要原因这种方法要是没接触过,我肯定是想不到的。不过搞acm的应该已经习惯了,算法本来就是很...神奇的东西。
做法:
首先是离散化,由于求解面积一类问题涉及到坐标值,而坐标值一般都是非interger的,故需要离散化。这个问题在之前那篇博文上已有介绍,便不赘述了。当然,我们还需要存储相关边的信息,以便于后续操作。
具体添加边的操作处理真的是很巧妙!
指导思想:一个矩形是由4条边构成的,两条与x平行的边+两条与y平行的边。矩形的面积正是由这4条边围成的。那么我们可以这样来求解面积,依次不断添加某一方向(与x平行或者与y平行)的边进来,每次添加一条边L1的时候观察之前添加进来的边L0中有没有与其能围成一个矩形的,有的话,我们就计算出这个矩形,并且将边L0消掉,因为L0已经与L1围成了一个矩形了,如果不消掉的话,势必会影响到后面的操作。
仅仅明白上述的思路还是不够,因为落实到代码中仍旧有些困难。难点大致如下:
1、边如何存储、如何添加?
2、如何判断有没有L0能与之围成矩形?(这个恐怕是最难解的问题了)
3、……
我其实真正想引出的是第二个问题。第一问是根据程序员自己的习惯和构思来的,没有固定的答案。第二问的解决思路是我认为真正巧妙的地方。我的处理预先处理与y平行的边时,开辟一个cover值,原矩形左边的边标记为1,右边的标记为-1 (想想为什么要标记为相反数)。在每次加入一个整条边的时候,会将这个整边划分拆开加入到线段树的每个节点中去,如果在某个叶子节点中(这里我更新到最底层再进行处理)我们发现他之前已经存储了边,那么一个新的小矩形就诞生了,计算面积之后我们再将现在的边覆盖掉之前的边。这个“覆盖”的动作是依靠边预处理的左、右标记cover值实现的。
具体cover值的妙处,还是要在代码中细细品味啊~
最后要感谢Accept童鞋耐心的指导,我才真正领悟了其中奥妙所在~ ^ ^ ~
覆盖的面积
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #define LL(x) x<<1 8 #define RR(x) x<<1|1 9 #define MAXN 1010 10 using namespace std; 11 double yy[2*MAXN]; 12 13 struct node1 14 { 15 double uy, dy, x; 16 int flag; 17 }line[2*MAXN]; 18 19 struct node2 20 { 21 double uy, dy, x; 22 int cover, mid; //cover值标记左右边情况 23 bool flag; //标记叶子节点 24 }tree[50*MAXN]; 25 26 int cmp(node1 a, node1 b) 27 { 28 return a.x<b.x; 29 } 30 31 void BuildTree(int index, int down, int up) 32 { 33 tree[index].mid = (down+up)>>1; 34 tree[index].cover = 0; 35 tree[index].dy = yy[down]; 36 tree[index].uy = yy[up]; 37 tree[index].flag = false; 38 tree[index].x = 0; 39 if(down+1 == up){ //到达最底层->叶子节点 40 tree[index].flag = true; 41 return ; 42 } 43 BuildTree(LL(index),down,tree[index].mid); 44 BuildTree(RR(index),tree[index].mid,up); 45 } 46 47 double Update(int index, double x, double dy, double uy, int cover) 48 { 49 double ans = 0; 50 if(tree[index].flag){ //更新到叶子节点 51 if(tree[index].cover>1){ //如果cover>1说明在该边添加之前已经有至少2条边加入了 52 //试想,满足这个条件说明肯定出现了覆盖过至少2次的面积 53 ans = (uy-dy)*(x-tree[index].x); 54 tree[index].cover += cover; //这里是关键点! 55 tree[index].x = x; 56 } 57 else{ //否则的话也需要更新 58 tree[index].cover += cover; 59 tree[index].x = x; 60 } 61 return ans; 62 } 63 else if(uy<=yy[tree[index].mid]) return Update(LL(index),x,dy,uy,cover); 64 else if(dy>=yy[tree[index].mid]) return Update(RR(index),x,dy,uy,cover); 65 else { 66 return Update(LL(index),x,dy,yy[tree[index].mid],cover)+Update(RR(index),x,yy[tree[index].mid],uy,cover); 67 } 68 } 69 70 int main() 71 { 72 int i, j, icase, n, cur; 73 double ans, a, b, c, d; 74 scanf("%d",&icase); 75 while(icase--){ 76 scanf("%d",&n); 77 cur = 0; 78 ans = 0; 79 for(i=0; i<n; ++i){ 80 scanf("%lf %lf %lf %lf",&a,&b,&c,&d); 81 yy[++cur] = b; 82 line[cur].dy = b; 83 line[cur].uy = d; 84 line[cur].flag = 1; 85 line[cur].x = a; 86 87 yy[++cur] = d; 88 line[cur].dy = b; 89 line[cur].uy = d; 90 line[cur].flag = -1; 91 line[cur].x = c; 92 //sum += (c-a)*(d-b); 93 } 94 sort(yy+1,yy+cur+1); 95 sort(line+1,line+cur+1,cmp); //都需要排序 96 BuildTree(1,1,cur); 97 for(i=1; i<=cur; ++i){ 98 ans += Update(1,line[i].x,line[i].dy,line[i].uy,line[i].flag); 99 } 100 printf("%.2lf\n",ans); 101 } 102 return 0; 103 }

浙公网安备 33010602011771号