HDU - 1542 Atlantis (线段树+扫描线)

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

题目大意:

给你若干个矩形的坐标(这些矩形可能是相互重叠的),问这些矩形所组成的图形的总面积。

解题思路:

扫描线的板子题。
我比较菜,扫描线学了大半天才稍微懂了那么一点点。
我认为要搞懂的有以下几点
1.我们记录下每一条,这条边有左右起点,高,和上边下边这么几个参数,至于为什么上边是-1 下边是1 下面再解释

struct node{
	double l,r,h;
	int d;//上边是-1 下边是1 
}a[maxn];

2.清楚用线段树维护的是什么。我们用线段树维护的是一个从最左边到最右边这个区间里的有效长度,啥叫有效长度呢?就是哪些区间内的长度还能构成矩形。
还维护了这段区间是不是被全覆盖了,就是维护的这个s。

struct Node{
	int l,r;
	int s;//标记覆盖情况
	double len;//区间有效长度 
}tree[maxn];

这是我们树的一个结点,这个结点存的是一条线段,s就是表示这条线段上是否全都存在下底边,len就是存的区间的有效长度,l,r是存的离散化后的的x位置的下标。
3.我们怎么更新这个线段树呢?我们在离散化排序去重后,开始扫我们存的每一条边,每条边都找到它在x数组中的位置,用二分找。

	for(int i=0;i<Cnt;i++){
			int l=lower_bound(x,x+len,a[i].l)-x+1;
			int r=lower_bound(x,x+len,a[i].r)-x;
			updata(l,r,a[i].d,1);
			sum+=(tree[1].len*(a[i+1].h-a[i].h));
		//	cout<<sum<<endl;
		}

我们更新的地方是 a[i].d,就是它上边下边的值,这就是为什么上边-1 下边1 了 下边的话就说明,这条边的区间可以用,可以构成矩形,所以就+1 上边就-1,重合的地方这个s值可能会是4啊5啊 表示这个边有好几条下边,就算减个上边,它依然是有效边。
https://www.baidu.com/link?url=x7YvvAcsBJLrsyVGIugn80sRghtjcjzZRJ7M-kMkiVA0QsaxGSdD3mulAXX7EvuR&wd=&eqid=8019567100032288000000035cbe601f
附上一个讲的比较好的文章。

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn =5e4+10;
double x[maxn];
int n;
///这个结构体存的是扫描线 
struct node{
	double l,r,h;
	int d;//上边是-1 下边是1 
}a[maxn];
//存树的节点 
struct Node{
	int l,r;
	int s;//标记覆盖情况
	double len;//区间长度 
}tree[maxn];

void build(int l,int r,int cnt){
	tree[cnt].l=l;
	tree[cnt].r=r;
	tree[cnt].s=0;
	tree[cnt].len=0;
	if(l==r) return ;
	else{
		int mid = (l+r)/2;
		build(l,mid,cnt*2);
		build(mid+1,r,cnt*2+1);
	}
}
void pushup(int cnt){
	if(tree[cnt].s)//全是下底 
	{
		tree[cnt].len=x[tree[cnt].r]-x[tree[cnt].l-1]; 
	}	
	///叶子节点 
	else if(tree[cnt].l==tree[cnt].r){
		tree[cnt].len=0.0; 
	}
	else{
		tree[cnt].len=tree[cnt*2].len+tree[cnt*2+1].len;
	}
}
void updata(int pl,int pr,int flag,int cnt){
	int l=tree[cnt].l;
	int r=tree[cnt].r;
	if(pl<=l && r<=pr){
		tree[cnt].s+=flag;
		pushup(cnt);
		return ;
	}
	int mid=(l+r)/2;
	if(pl<=mid) updata(pl,pr,flag,cnt*2);
	if(pr>mid) updata(pl,pr,flag,cnt*2+1);
	pushup(cnt); 
}
bool cmp(struct node x,struct node y){
	return x.h<y.h;
}
int main()
{
	int cas=1;
	while(cin>>n&&n){
		int Cnt=0;
		double SUM=0.0;
		for(int i=1;i<=n;i++){
			double x1,x2,y1,y2;
			cin>>x1>>y1>>x2>>y2;
		
			if(y1>y2) swap(y1,y2);
			SUM=SUM+abs((x2-x1))*abs((y2-y1));
			a[Cnt].l=x1,a[Cnt].r=x2,a[Cnt].h=y1,a[Cnt].d=1;
			x[Cnt++]=x1;
			
			a[Cnt].l=x1,a[Cnt].r=x2,a[Cnt].h=y2,a[Cnt].d=-1;
			x[Cnt++]=x2;
		}
		sort(x,x+Cnt);
		sort(a,a+Cnt,cmp);
		int len=unique(x,x+Cnt)-x;//去重

		build(1,len,1);
		double sum=0.0;
		for(int i=0;i<Cnt;i++){
			int l=lower_bound(x,x+len,a[i].l)-x+1;
			int r=lower_bound(x,x+len,a[i].r)-x;
			updata(l,r,a[i].d,1);
			sum+=(tree[1].len*(a[i+1].h-a[i].h));
		//	cout<<sum<<endl;
		}
		 printf("Test case #%d\nTotal explored area: %.2f\n\n", cas++, sum);

	}	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43179892/article/details/89465362
今日推荐