Atlantis(hdu1151,求面积并,线段树+离散化+扫描)

/*http://poj.org/problem?id=1151

Atlantis

Time Limit: 1000MS  Memory Limit: 10000K

Total Submissions: 14468  Accepted: 5551

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

Source

Mid-Central European Regional Contest 2000

思路:

求出矩形并的面积

线段数+离散化+扫描线

照抄的模板的

*/

2011-04-24 13:48

hdu 1542 pku 1151 Atlantis 【线段树求矩形并】

hdu 1542 pku 1151 Atlantis 【线段树求矩形并】

【题意】:

线段树经典应用,求矩形并的面积

【分析】:

第一道矩形几何题,主要是练习线段树来的

搞了蛮久的,主要是卡在如何用线段树来维护这个问题上了

写了小结分析

求矩形的并,由于矩形的位置可以多变,因此矩形的面积一下子不好求

这个时候,可以采用分割的思想,即把整块的矩形面积分割成几个小矩形的面积,然后求和就行了

这里我们可以这样做,把每个矩形投影到 坐标轴上来(投影到 轴上去也是可以的,投影到 轴上去的话,就相当于矩形是竖着切的)

然后我们可以枚举矩形的 坐标,然后检测当前相邻 坐标上 方向的合法长度,两种相乘就是面积

然后关键就是如何用线段树来维护那个 合法长度

可以这样来搞

线段树的节点这样定义

struct node { int left,right,cov; double len; }

cov 表示当前节点区间是否被覆盖,len 是当前区间的合法长度

然后我们通过扫描线的方法来进行扫描

枚举 的竖边,矩形的左边那条竖边就是入边,右边那条就是出边了

然后把所有这些竖边按照 坐标递增排序,每次进行插入操作

由于坐标不一定为整数,因此需要进行离散化处理

每次插入时如果当前区间被完全覆盖,那么就要对 cov 域进行更新

入边 +1 出边 -1

更新完毕后判断当前节点的 cov 域是否大于 0

如果大于 0,那么当前节点的 len 域就是节点所覆盖的区间

否则

如果是叶子节点,则 len=0

如果内部节点,则 len=左右儿子的 len 之和

以上来自:http://hi.baidu.com/fantasyni/item/b5b6f21b9d9353503b176e05

下面代码搬了模板的:

#include<stdio.h>
#include<iomanip>
#include<cmath>
#include<string.h>
#include<algorithm>
#include <iostream>
using namespace std;
const int maxn=204;
struct node
{
int st,ed,c;//c:被覆盖层数,m区间测度
  double m;
}tr[maxn*4];
struct Line//定义纵方直线
{
double x,y1,y2;
bool s;//s=1.左直线,s=0右直线
}line[maxn];
double y[maxn],ty[maxn];//用于离散化纵坐标
//建立线段树
void build(int root,int st,int ed)
 {tr[root].st=st;
tr[root].ed=ed;
 tr[root].c=0;
tr[root].m=0;
if(ed-st>1)
 {int mid=(st+ed)/2;
 build(root*2,st,mid);
 build(root*2+1,mid,ed);
 }
  }
inline void updata(int root)//更新节点............
{
 if(tr[root].c>0)//如果存在覆盖
 tr[root].m=y[tr[root].ed-1]-y[tr[root].st-1];
 else if(tr[root].ed-tr[root].st==1)
   tr[root].m=0;
else
 tr[root].m=tr[root*2].m+tr[root*2+1].m;
}
void insert(int root,int st,int ed)
{
if(st<=tr[root].st&&tr[root].ed<=ed)
 {
tr[root].c++;
  updata(root);
return;
 }
 if(tr[root].st-tr[root].ed==-1)return;
int mid=(tr[root].st+tr[root].ed)/2;
if(st<mid)insert(root*2,st,ed);
 if(ed>mid)insert(root*2+1,st,ed);
updata(root);
 }
void Delete(int root,int st, int ed)
{
if(st<=tr[root].st&&tr[root].ed<=ed)
 {
tr[root].c--;
  updata(root);
return;
 }
 if(tr[root].st-tr[root].ed==-1)return;
int mid=(tr[root].st+tr[root].ed)/2;
if(st<mid)Delete(root*2,st,ed);
 if(ed>mid)Delete(root*2+1,st,ed);
updata(root);
}
int Correspond(int n,double t )//利用二分查找出t,在y[]中 所对应的位置
{int low,high,mid;
  low=0;
high=n-1;
while(low<high)
 {
 mid=(low+high)/2;
if(t>y[mid])
 low=mid+1;
else
 high=mid;
 }
return high+1;
}
bool cmp(Line l1,Line l2)
{
return l1.x<l2.x;
}
int main()
{
int n,i,num,l,r,c=0;
 double area,x1,x2,y1,y2;
while(scanf("%d",&n)!=EOF&&n!=0)
 {
  for(i=0;i<n;i++)
  {
  scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
 line[2*i].x=x1;line[2*i].y1=y1;
 line[2*i].y2=y2;line[2*i].s=1;
line[2*i+1].x=x2;line[2*i+1].y1=y1;
 line[2*i+1].y2=y2;line[2*i+1].s=0;
 ty[2*i]=y1;
ty[2*i+1]=y2;
  }
 n=n*2;
 sort(line,line+n,cmp);//按照从到右顺序
 sort(ty,ty+n);//从小到大顺序排列
 y[0]=ty[0];
for(i=num=1;i<n;i++)//进行离散化处理
  {if(ty[i]!=ty[i-1])
   {y[num++]=ty[i];}
  }
 build(1,1,num);//将离散化后的纵坐标进行建树
  area=0;
//for(i=0;i<num;i++)
 // printf("%.2lf\n",y[i]);
for(i=0;i<n-1;i++)
  {
  l=Correspond(num,line[i].y1);//线段两端对应的y[]上的坐标值
   r=Correspond(num,line[i].y2);
 if(line[i].s)
insert(1, l, r);
 else //删除矩形的右边
 Delete(1, l, r);
 area += tr[1].m * (line[i+1].x - line[i].x); }
printf("Test case #%d\n",++c);
 printf("Total explored area: %.2lf\n\n",area);
 }
 return 0;
}


你可能感兴趣的:(Atlantis(hdu1151,求面积并,线段树+离散化+扫描))