POJ 1151 Atlantis

POJ 1151 Atlantis

线段树+离散化+矩形面积求并。做完了前面比较简单的 POJ3277,再做这一题就知道怎么下手了。开始的时候我只打印一个算的面积,然后很果断的wrong了,之后就没想这道题目了,当我做其它题目的时 候,突然想起来,我没按照格式输出啊(狂晕)。然后我又将输出结果改了一下,一交PE了。哇咔咔,忽略了空行,。加了空行就过了,。

     说下这个题目的思路,先离散化,由于只有200个矩形,也就 是最多只有400个点,(x轴算),那么我们就可以将与y轴平行的边投影成点,然后将这些点排个序,去除重复的点,然后以这些点来建线段树。线段树中保 存当前矩形的上下边的坐标以及对应的区间的和。建树的方式和POJ3277相同,以下标建树。用个数组来保存对应值。然后我们将原来的矩形按照y轴的坐标 进行排序,要求底边坐标小的在前,相同的坐标,上边小的在前。

   然后我们不断的插入矩形。从前往后插入。设现在有矩形ab如果a,b在同一个区间内(a,bx坐标相同),那么先插入的必定是y坐标比较小的,后插入的是y坐标比较大的,如果已经插入矩形a 当插入矩形b的时候,如果a,b无共同区域,也即不相交,此时我们需要求出先插入的矩形a的面积,并累加到当前的线段树节点中,然后我们用带插入的矩形来更新当前节点的值。而如果两个矩形有相同的区域,那么必定可以将两个矩形合并成一个大的矩形,(因为两个矩形的x坐标是相同的,而y坐 标不同)。而如果插入的时候, abx坐标范围只是部分重叠,那么我们需要先将当前节点的a更新到子树中,而在子树中只可能出现如下情况:如果子树区间中已经有矩形,那么该矩形的x坐标范围一定是当前节点中的x坐标区间范围,那么我们就可以向前面那样,分成两种情况,两个矩形有共同区间与没有共同区间。如果子树区间中没有矩形,我们直接将其插入到其中即可。然后在将需要插入的树,插入即可。

   统计的时候,我们只需要累加每个节点的sum值,对于每个元线段,我们直接按照矩形的面积计算公式计算,而非元线段我们就需要将父节点的值更新到子节点,然后在统计。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>

using namespace std ;

#define MAXN 10000

struct Rectangle
{
	double x1 ;
	double x2 ;
	double y1 ;
	double y2 ;
};

struct SegmentTree
{
	int l ;
	int r ;
	double bottom ;
	double top ;
	double sum ;
};

bool cmp(Rectangle a , Rectangle b){
	if(a.y1 != b.y1)
		return a.y1 < b.y1 ;
	else
		return a.y2 < b.y2 ;
}

Rectangle line[MAXN] ;
SegmentTree tree[MAXN] ;
double Sline[MAXN] ;

int n ;
int m ;


bool input(){
	scanf("%d" , &n) ;

	m = 0 ;
	for(int i = 1 ; i <= n ; i ++){
		scanf("%lf%lf%lf%lf" , &line[i].x1 , &line[i].y1 , &line[i].x2 , &line[i].y2) ;
		Sline[++m] = line[i].x1 ;
		Sline[++m] = line[i].x2 ;
	}

	return n ;
}
int inline M(int x , int y){
	return (x + y)>>1 ;
}
int inline L(int x){
	return x<<1 ;
}
int inline R(int x){
	return x<<1|1 ;
}

void build(int l , int r , int c){
	tree[c].l = l ;
	tree[c].r = r ;
	tree[c].sum = 0.0 ;
	tree[c].bottom = 0.0 ;
	tree[c].top = 0.0 ;

	if(l + 1 == r)
		return ;

	build(l , M(l , r) , L(c)) ;
	build(M(l , r) , r , R(c)) ;
}

void pushdown(int c){
	int l = L(c) ;
	int r = R(c) ;
    //如果两个矩形不重合
	if(tree[c].bottom >= tree[l].top){
		tree[l].sum += (Sline[ tree[l].r ] - Sline[ tree[l].l ]) * (tree[l].top - tree[l].bottom) ;
		tree[l].top = tree[c].top ;
		tree[l].bottom = tree[c].bottom ;
	}
	//有相同的区域,合并成一个新的矩形
	else if(tree[c].bottom > tree[l].bottom	 && tree[c].bottom	< tree[l].top)	{
		tree[l].top = tree[l].top > tree[c].top ? tree[l].top : tree[c].top ;
		tree[l].bottom = tree[l].bottom > tree[c].bottom ? tree[c].bottom : tree[l].bottom ;
	}

	if(tree[c].bottom >= tree[r].top){
		tree[r].sum += (Sline[ tree[r].r ] - Sline[tree[r].l]) *(tree[r].top - tree[r].bottom) ;
		tree[r].top = tree[c].top ;
		tree[r].bottom = tree[c].bottom ;
	}
	else if(tree[c].bottom	> tree[r].bottom && tree[c].bottom	< tree[r].top){
		tree[r].top = tree[c].top > tree[r].top ? tree[c].top : tree[r].top ;
		tree[r].bottom = tree[r].bottom > tree[c].bottom ? tree[c].bottom : tree[r].bottom ;
	}

	tree[c].top = 0.0 ;
	tree[c].bottom = 0.0 ;
}

void insert(double l , double b , double r , double t , int c){
		//如果插入的区间是相同的,我们就比较两个矩形是不是有重合的
        //区间,如果有则更新成一个新的矩形,否则将之前插入的矩形的
        //面积计算出来并累加到当前节点的sum值中,然后更新一下当前的节点的矩形
		if( l == Sline[ tree[c].l ] && r == Sline[ tree[c].r ] ){
				if(tree[c].bottom <= b && tree[c].top >= b){
					if(tree[c].top < t)
						tree[c].top = t ;
				}
				else if(tree[c].top < b){
					tree[c].sum += (Sline[ tree[c].r ] - Sline[ tree[c].l ])*(tree[c].top - tree[c].bottom) ;
					tree[c].bottom = b ;
					tree[c].top = t ;
				}
				return ;
		}

		if(tree[c].l + 1 == tree[c].r)
				return ;
        //更新子节点的值
		pushdown(c) ;

		int m = M(tree[c].l , tree[c].r) ;
		if(Sline[m] >= r){
			insert(l , b , r , t , L(c) ) ;
		}
		else if(Sline[m] <= l){
			insert(l , b , r , t , R(c) ) ;
		}
		else {
			insert(l , b , Sline[m] , t , L(c)) ;
			insert(Sline[m] , b , r , t , R(c)) ;
		}
}
void query(double &ans , int c){
	//累加每个节点的sum值
	ans += tree[c].sum ;
    //只统计元线段中的值
	if(tree[c].l + 1 == tree[c].r){
	    ans += (tree[c].top - tree[c].bottom) *( Sline[ tree[c].r ] - Sline[ tree[c].l ] ) ;
		return ;
	}
	//向下更新
    pushdown(c) ;

	query(ans , L(c)) ;
	query(ans , R(c)) ;
}
void solve(){
    //进行里离散化+排序
	sort(Sline + 1 , Sline + m + 1) ;
	sort(line  + 1 , line  + n + 1 , cmp) ;
	m = unique(Sline + 1 , Sline + m + 1) - Sline - 1 ;

	build(1 , m , 1) ;

	for(int i = 1 ; i <= n ; i ++){
		insert(line[i].x1 , line[i].y1 , line[i].x2 , line[i].y2 , 1) ;
	}

	double ans = 0.0 ;
	query(ans , 1) ;
	printf("Total explored area: %.2lf\n" , ans) ;
}


int main(){
    int t ;
    t = 1 ;

	while(input()){
        printf("Test case #%d\n" , t++) ;
		solve() ;
        printf("\n") ;
	}

	return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值