## poj1151(线段树+离散化)

2017年11月12日 算法 ⁄ 共 3593字 ⁄ 字号 评论关闭

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 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 `

求矩形面积的并；

步骤：

（1）对所有的矩阵的纵坐标集合排序后进行离散化建树

（2）构建扫描线，如图:

图中红色的竖线代表扫描线，结合程序看，第一次调用Updata(1,line(1))进行更新的时候，最左边的红色的线段的c（覆盖）=1；也就是有效线段，通过这个有效线段求出了图中的黑色区域的面积，然后第二次调用更新函数，此时第二条红色的扫描线段的下半部分（第一条红色线段有标记把扫描线分上下两部分）的c现在为2（因为第一次已经覆盖了一次c就为1了），然后把所有c大于1的线段加起来就为当前的有效线段长度（长度为第二条扫描线长度加上第一条扫描线的下半部分，因为之前为1），求得紫色的部分面积，然后第三次调用更新函数，从新计算几部分线段的c值（注意此时的扫描线段的flag为-1，对c有减的作用，当这条线段的c为0后，就不加这条线段到有效的长度中了），第四次调用扫描线段数因为所有的线段的c都为0了，所以有效长度为0

CODE(有注释)

```#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
#define MAXN 205
typedef struct
{
int l;
int r;
int c;/*记录有效长度是否被覆盖*/
double cnt,lf,rf;/*有效长度cnt，lf，lr分别表示原图上真正的长度*/
}Node;/*节点*/
Node segtree[3*MAXN];/*开的空间，已经离散化了，所以一般区间开的大小为矩形点总个数的3倍*/
typedef struct
{
double x;  /*垂直以x轴的扫描线与y轴的距离*/
double y1,y2;
int flag;/*标记每个矩形左边的扫描线为1，右边的扫描线为-1，便于计算c*/
} Line;
Line line[MAXN];
double Y[MAXN];/*便于等一下映射到线段树里面的lf与rf中*/
bool cmp(Line a,Line b)
{
return a.x<b.x;
}
void Build_segtree(int t,int l,int r)/*建立线段树*/
{
int mid;
segtree[t].l=l;
segtree[t].r=r;
segtree[t].c=segtree[t].cnt=0;
segtree[t].lf=Y[l];
segtree[t].rf=Y[r];
if(l+1==r)return;
mid=(l+r)/2;
Build_segtree(t*2,l,mid);
Build_segtree(t*2+1,mid,r);
}
void compute(int t)/*通过逐步回溯来计算 segtree[t].cnt，一直回溯t==1，然后求出了有效的长度cnt*/
{
if(segtree[t].c>0)
{
segtree[t].cnt=segtree[t].rf-segtree[t].lf;
return;
}
if(segtree[t].l+1==segtree[t].r)segtree[t].cnt=0;
else segtree[t].cnt=segtree[t*2].cnt+segtree[t*2+1].cnt;
}
void Updata(int t,Line cur)/*扫描更新c值，然后就是递归然后回溯计算有效长度*/
{
Line temp;
if(cur.y1==segtree[t].lf&&cur.y2==segtree[t].rf)
{
segtree[t].c+=cur.flag;
compute(t);
return;
}
if(cur.y2<=segtree[t*2].rf)Updata(t*2,cur);
else if(cur.y1>=segtree[t*2+1].lf)Updata(t*2+1,cur);
else
{
temp=cur;
temp.y2=segtree[t*2].rf;
Updata(t*2,temp);
temp=cur;
temp.y1=segtree[t*2+1].lf;
Updata(t*2+1,temp);
}
compute(t);
}
int main()
{
int i,n,icase=0;
int t;
double ans;
double x1,y1,x2,y2;
while(scanf("%d",&n)!=-1&&n)
{
icase++;
t=1;
for(i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[t].x=x1;
line[t].y1=y1;
line[t].y2=y2;
line[t].flag=1;
Y[t]=y1;
t++;
line[t].x=x2;
line[t].y1=y1;
line[t].y2=y2;
line[t].flag=-1;
Y[t]=y2;
t++;
}
sort(line+1,line+t,cmp);/*关于x进行排序，确定扫描线的相对位置*/
sort(Y+1,Y+t);/*对于y排序，便于等一下建树分段时候的映射*/
Build_segtree(1,1,t-1);
Updata(1,line[1]);
ans=0;
for(i=2;i<t;i++)
{
ans+=segtree[1].cnt*(line[i].x-line[i-1].x);/*把每一段求和相加*/
Updata(1,line[i]);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",icase,ans);
}
return 0;
}
```