(线段树——扫描线)hdoj 1542-Atlantis

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. 

InputThe 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.OutputFor 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 

建立结构体记录每个矩形的情况,用3个double类型变量存储左、右、高。额外用一个变量记录是上底还是下底。由于各个数据都可能是浮点数,需要离散化,离散化时将所有“左”,“右”放入一个数组,
排序去重,再建立一个采用二分法的搜索函数,返回每个值对应的离散化后的数值。用于update的每个l和r的值表示 离散化数组x中 该编号的点到下一个编号的点连线形成的线段。这是因为例如如下的
一条线段

如果按照数组中每个表示一个点来看的话,改变[2,5]时 mid=3 向下进行的update为 [2,3] [4,5]而中间缺少了[3,4]这个区间。但是如果改成离散化数组中每个编号表示一个线段,中间就不会产生
这样的空白。
线段树每个结点表示该结点记录的区间内有多少此刻被标记(即应该算在矩形面积其中的)的线段。扫描线从所有矩形的底边最下面的一条开始进行,每次更新线段树,得到当前的底。这个底一直到下一个
所有矩形中的底出现前,一直都是整个面积中的一部分。即以这个底和 “当前的底与下一个出现的底之间的距离”构成的“若干个矩形”的面积是整体的一部分。将这部分加上,再去看下一个出现边的位置进行
更新。直到进行到最后一条底停止。(这里把每个矩形的上底、下底都叫做底)
  1 #include <iostream>
  2 //#include<bits/stdc++.h>
  3 #include <stack>
  4 #include <queue>
  5 #include <map>
  6 #include <set>
  7 #include <cstdio>
  8 #include <cstring>
  9 #include <algorithm>
 10 #include <math.h>
 11 using namespace std;
 12 const int MAX=210;
 13 struct node//线段树的结点
 14 {
 15     int l,r;
 16     int mark;//标记情况,为正时计算这部分,非正时不计算(在图中这个高度处此区间为空白)
 17     double sum;//区间要计算的线段的长度
 18 }st[10*MAX];
 19 struct rectangle
 20 {
 21     double l,r,h;//矩形的左、右、高
 22     int d;//1表示为下边,-1表示为上边
 23     rectangle(){}
 24     rectangle(double l1,double r1,double h1,int d1):l(l1),r(r1),h(h1),d(d1){};
 25     bool operator < (const rectangle &x)const
 26     {
 27         return h<x.h;
 28     }
 29 }seg[MAX];
 30 double hashh[2*MAX];//进行离散化的数组,将每个double值离散化为此数组的下标进行线段树操作
 31 void init(int l,int r,int k)//初始化线段树
 32 {
 33     st[k].l=l;
 34     st[k].r=r;
 35     st[k].sum=st[k].mark=0;
 36     if(l==r)
 37         return;
 38     init(l,(l+r)/2,2*k);
 39     init((l+r)/2+1,r,2*k+1);
 40 }
 41 void pushup(int k)//更新此k编号的线段树结点
 42 {
 43     if(st[k].mark)
 44         st[k].sum=hashh[st[k].r+1]-hashh[st[k].l];
 45     else if(st[k].r==st[k].l)//未被标记的叶子结点就表示一条线段,不计算在其中,故sum此为0
 46         st[k].sum=0;
 47     else
 48         st[k].sum=st[2*k].sum+st[2*k+1].sum;
 49 }
 50 void update(int ul,int ur,int l,int r,int k,int d)//更新操作
 51 {
 52     if(l>=ul&&r<=ur)
 53     {
 54         st[k].mark+=d;
 55         pushup(k);
 56         return;
 57     }
 58     int mid=l+r>>1;
 59     if(ul<=mid)
 60         update(ul,ur,l,mid,2*k,d);
 61     if(ur>mid)
 62         update(ul,ur,mid+1,r,2*k+1,d);
 63     pushup(k);
 64 }
 65 int binarysearch(double k,double *x,int n)//二分查找数列中对应k的编号
 66 {
 67     int l=0,r=n-1;
 68     int mid;
 69     while(l<=r)
 70     {
 71         mid=l+r>>1;
 72         if(x[mid]==k)
 73             return mid;
 74         else if(x[mid]<k)
 75             l=mid+1;
 76         else
 77             r=mid-1;
 78     }
 79     return -1;
 80 }
 81 int main()
 82 {
 83     int n,num1,num2;
 84     double x1,y1,x2,y2;
 85     int ge=0;
 86     while(cin>>n,n)
 87     {
 88         num1=0;
 89         num2=1;
 90         for(int i=0;i<n;i++)//读入数据
 91         {
 92             cin>>x1>>y1>>x2>>y2;
 93             hashh[num1]=x1;
 94             seg[num1++]=rectangle(x1,x2,y1,1);
 95             hashh[num1]=x2;
 96             seg[num1++]=rectangle(x1,x2,y2,-1);
 97         }
 98         sort(seg,seg+num1);//排序
 99         sort(hashh,hashh+num1);
100         for(int i=1;i<num1;i++)//去重
101         {
102             if(hashh[i]!=hashh[i-1])
103                 hashh[num2++]=hashh[i];
104         }
105         init(0,num2-1,1);//建立线段树,由于总共0,1,……num2-1为编号的hashh数组有值,树的大小建为 [0,num2-1]
106         int L,R;
107         double ans=0;
108         for(int i=0;i<num1;i++)
109         {
110             L=binarysearch(seg[i].l,hashh,num2);
111             R=binarysearch(seg[i].r,hashh,num2)-1;//由于值表示每个线段的左顶点,故R需要-1
112             update(L,R,0,num2-1,1,seg[i].d);
113             ans+=st[1].sum*(seg[i+1].h-seg[i].h);//st[1].sum就是整个大区间的需要计算的线段长度之和
114 //            cout<<ans<<"
";
115         }
116         printf("Test case #%d
Total explored area: %.2f

",++ge,ans);
117         }
118         return 0;
119 }


 
原文地址:https://www.cnblogs.com/quintessence/p/6488947.html