【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 }

 

posted on 2012-08-14 10:08  Yuna_  阅读(77)  评论(0)    收藏  举报