hdu1542 Atlantis

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;
}

时间: 2024-10-12 16:11:08

hdu1542 Atlantis的相关文章

HDU-1542 Atlantis 【线段树+扫描线】

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. You

hdu-1542 Atlantis(离散化+线段树+扫描线算法)

题目链接: Atlantis Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 65536/32768 K (Java/Others) Problem Description There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include ma

hdu1542 Atlantis (线段树+扫描线+离散化)

Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 9032    Accepted Submission(s): 3873 Problem Description There are several ancient Greek texts that contain descriptions of the fabled i

hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> #include <algorithm> #define MAXN 110 #define LL ((rt<<1)+1) #define RR ((rt<<1)+2) using namespace std; int n; struct segment{ double l

线段树题目总结

一.单点更新 1.hdu1166 敌兵布阵:有N个兵营,每个兵营都给出了人数ai(下标从1开始),有四种命令,(1)"Addij",表示第i个营地增加j人.(2)"Sub i j",表示第i个营地减少j人.(3)"Query ij",查询第i个营地到第j个营地的总人数.(4)"End",表示命令结束.解题报告Here. 2.hdu1754 I Hate It:给你N个数,M个操作,操作分两类.(1)"QAB"

数据结构---线段树

线段树 转载请注明出处,谢谢!http://blog.csdn.net/metalseed/article/details/8039326  持续更新中···   一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树

(转载)线段树模板(来自胡浩大牛)

http://www.notonlysuccess.com/(今天看二叉树,想回来看看,发现大牛博客进不去...) 如果要学,就要好好学.我copy的,如有错,请看http://www.cnblogs.com/Mu-Tou/archive/2011/08/11/2134427.html [完全版]线段树 很早前写的那篇线段树专辑至今一直是本博客阅读点击量最大的一片文章,当时觉得挺自豪的,还去pku打广告,但是现在我自己都不太好意思去看那篇文章了,觉得当时的代码风格实在是太丑了,很多线段树的初学者

线段树总结 (转载 里面有扫描线类 还有NotOnlySuccess线段树大神的地址)

转载自:http://blog.csdn.net/shiqi_614/article/details/8228102 之前做了些线段树相关的题目,开学一段时间后,想着把它整理下,完成了大牛NotOnlySuccess的博文“完全版线段树”里的大部分题目,其博文地址Here,然后也加入了自己做过的一些题目.整理时,更新了之前的代码风格,不过旧的代码仍然保留着. 同样分成四类,不好归到前四类的都分到了其他.树状数组能做,线段树都能做(如果是内存限制例外),所以也有些树状数组的题目,会标示出来,并且放

线段树——转

  一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍 2:基本操作(demo用的是查询区间最小值) 线段树的主要操作有: (1):线段树的构造 void build(int node,