码迷,mamicode.com
首页 > 其他好文 > 详细

hdu1542 Atlantis

时间:2015-05-22 17:07:41      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:线段树

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

这道题我想了很久,终于A了。。题目是求一些矩形合起来的面积,可以用线段树做。首先用结构体s记录所有矩形的上下边,记录边的左端点x2,右端点x3,纵坐标y,还有增量f(增量的意思是扫到上边的时候总区间减去这条边,扫到下边的时候总区间加上这条边,用1,-1表示),因为线段的坐标都是浮点数,所以要离散化。然后再开一个数组pos,记录所有线段的横坐标,排序后,判断是否有重复值,如果有就跳过,这样做是为了待会建树时方便,因为相同的值建树时没有用。然后建立线段树,用l,r维护每条线段的左右端点,cnt维护这条线段被重复覆盖了几次,0代表没有线段覆盖,大于1表示多次覆盖,len 表示这段区间被覆盖区间的实际总长度,这里对应的是真实坐标的值,并不是线段树中的整数。这道题的线段树比较特别,可以用简单dp做,只有这道题适用,不适用其他题,因为这里保证了有下边就一定有相同长度的上边。

还有最重要的一点(我这里卡了半天= =):就是更新的时候r-1,算总长度的时候r+1;

解释:因为建立线段树的时候我们子区间是这样建的mid=(l+r)/2;build(l,mid),build(mid+1,r),因为对于单纯的线段来说,子区间加起来就是整条线段,但是我们这里不是这样的,因为我们离散化后,mid,mid+1之间也有距离的!所以如果总长度如果由子区间两端加起来其实是pos[r]-pos[mid+1]+pos[mid]-pos[l],少了一段mid~mid+1.所以这里有一个技巧,就是更新的时候r-1,算总长度的时候r+1;

举个例子:要更新的是[1,5],那么我们更新[1,4],然后算的时候算[1,3],[3,4],[4,5],这样就把中间的问题解决了。

动手画一下,就懂了。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
struct node{
	double x2,x3,y;
	int f;
}s[800];
double pos[800];
int m;

struct edge{
	int l,r,cnt;
	double len;
}b[800];

bool cmp(node a,node b){
	return a.y<b.y;
}

int find(double x)
{
	int mid,l=1,r=m;
	while(l<=r)
	{
		mid=(l+r)/2;
		if(pos[mid]>x)r=mid-1;
		else if(pos[mid]<x)l=mid+1;
		else return mid;
	}
	if(pos[l]==x)return l;
	else return r;
}

void build(int l,int r,int i)
{
	int mid;
	b[i].l=l;b[i].r=r;b[i].cnt=b[i].len=0;
	if(l==r)return;
	mid=(l+r)/2;
	build(l,mid,i*2);
	build(mid+1,r,i*2+1);
}

void getline(int i)
{
	if(b[i].cnt)b[i].len=pos[b[i].r+1]-pos[b[i].l];
	else if(b[i].l==b[i].r)b[i].len=0;
	else b[i].len=b[i*2].len+b[i*2+1].len;
}

void update(int l,int r,int add,int i)
{
	int mid;
	if(b[i].l==l && b[i].r==r){
		b[i].cnt+=add;
		getline(i);return;
	}
	mid=(b[i].l+b[i].r)/2;
	if(r<=mid)update(l,r,add,i*2);
	else if(l>mid)update(l,r,add,i*2+1);
	else {
		update(l,mid,add,i*2);
		update(mid+1,r,add,i*2+1);
	}
	getline(i);
}

int main()
{
	int n,i,j,num,l,r,k=0;
	double x2,y2,x3,y3,sum;
	while(scanf("%d",&n)!=EOF && n!=0)
	{
		num=0;sum=0;
		for(i=1;i<=n;i++){
			scanf("%lf%lf%lf%lf",&x2,&y2,&x3,&y3);
			num++;
			s[num].x2=x2;s[num].x3=x3;s[num].y=y2;s[num].f=1;pos[num]=x2;
			num++;
			s[num].x2=x2;s[num].x3=x3;s[num].y=y3;s[num].f=-1;pos[num]=x3;
		}
		sort(s+1,s+1+num,cmp);
		sort(pos+1,pos+1+num);
		m=1;
		for(i=2;i<=num;i++){
			if(pos[i]!=pos[i-1]){
				m++;pos[m]=pos[i];
			}
		}
		build(1,m,1);
		s[0].y=0;
		for(i=1;i<=num;i++){
			sum+=b[1].len*(s[i].y-s[i-1].y);
			l=find(s[i].x2);
			r=find(s[i].x3)-1;
			update(l,r,s[i].f,1);
		}
		k++;
		printf("Test case #%d\n",k);
		printf("Total explored area: %.2f\n\n",sum);
	}
	return 0;
}


hdu1542 Atlantis

标签:线段树

原文地址:http://blog.csdn.net/kirito_acmer/article/details/45918499

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!