矩形面积并

来源:互联网 发布:详解进化论知乎 编辑:程序博客网 时间:2024/05/17 02:43

hdu1542 

Atlantis

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 14285 Accepted Submission(s): 5896


Problem Description
There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.


Input
The input file consists of several test cases. Each test case starts with a line containing a single integer n (1<=n<=100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0<=x1<x2<=100000;0<=y1<y2<=100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.

The input file is terminated by a line containing a single 0. Don’t process it.


Output
For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.

Output a blank line after each test case.


Sample Input
2
10 10 20 20
15 15 25 25.5
0


Sample Output
Test case #1
Total explored area: 180.00


#include <stdio.h>  #include <string.h>  #include <iostream>  #include <algorithm>  using namespace std;  const int SIZE=505;  int add[SIZE<<2];                               //add为区间标记,与懒惰标记类似  double x[SIZE<<2],sum[SIZE<<2];  struct node{      int ss;                                     //ss=1为下边,ss=-1为上边      double l,r,h;                               //分别记录线段的左端,右端和高度      node(){}      node(double a,double b,double c,int d):l(a),r(b),h(c),ss(d){}      friend bool operator<(node p,node q){       //因为是从下到上扫描因此,需要按照高度排序          return p.h<q.h;      }  }s[SIZE];  void pushup(int rt,int l,int r){      if(add[rt])                                 //这就是如何解决的线段重叠的办法,如果当前区间      sum[rt]=x[r+1]-x[l];                        //有标记用x数组更新,而不是左右儿子更新      else if(l==r)      sum[rt]=0;      else      sum[rt]=sum[rt<<1]+sum[rt<<1|1];            //与懒惰标记的区别在于标记不会消失(除非遇到上边)  }                                               //这也就是线段不会重复相加减的关键  void update(int L,int R,int c,int l,int r,int rt){      int m;      if(L<=l&&r<=R){          add[rt]+=c;                             //update与普通线段树基本相同          pushup(rt,l,r);          return;      }      m=(l+r)>>1;      if(L<=m)      update(L,R,c,l,m,rt<<1);      if(R>m)      update(L,R,c,m+1,r,rt<<1|1);      pushup(rt,l,r);  }  int main(){                                     //用的是从下到上扫描,也就是扫描线,又因为      int n,i,k,l,m,r,cas;                        //x坐标可能过多,因此需要对x轴进行离散化,从      double a,b,c,d,ans;                         //下到上扫描也就是遇到下边进行成段加1,遇到      cas=1;                                      //上边就是成段减1,所以问题就变成了线段树成      while(scanf("%d",&n)!=EOF&&n){              //段更新,但我们还需要解决线段重叠的问题,只          k=1,ans=m=0;                            //要修改pushup就能巧妙的解决这个问题          for(i=0;i<n;i++){              scanf("%lf%lf%lf%lf",&a,&b,&c,&d);              x[m]=a;              s[m++]=node(a,c,b,1);              x[m]=c;              s[m++]=node(a,c,d,-1);          }          sort(x,x+m);          sort(s,s+m);          for(i=1;i<m;i++)                        //对x坐标进行离散化,也可以用set          if(x[i]!=x[i-1])          x[k++]=x[i];          memset(add,0,sizeof(add));          memset(sum,0,sizeof(sum));          for(i=0;i<m-1;i++){              l=lower_bound(x,x+k,s[i].l)-x;              r=lower_bound(x,x+k,s[i].r)-x-1;    //找出更新的区间,这是左必右开区间,因此              update(l,r,s[i].ss,0,k-1,1);        //r需要减1,这也是与普通线段树的区别,普通              ans+=(sum[1]*(s[i+1].h-s[i].h));    //的线段树其实是更新的节点中保存的点,假如          }                                       //对[1,2]和[2,3]区间都加1,普通线段树中2          printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++,ans);      }                                           //那个点会变成2,而这个是表示的线段,因此      return 0;                                   //2应该为1,因此用左闭右开区间  }