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

参考:http://blog.youkuaiyun.com/xingyeyongheng/article/details/8927732

#include<iostream>  
#include<cstdio>  
#include<cstdlib>  
#include<cstring>  
#include<string>  
#include<queue>  
#include<algorithm>  
#include<map>  
#include<iomanip>  
#define INF 99999999  
using namespace std;

const int MAX = 200 + 10;注意区间大小,x坐标点数为长方形个数的2倍
int mark[MAX << 2];//记录某个区间的下底边个数  
double sum[MAX << 2];//记录某个区间的下底边总长度  
double Hash[MAX];//对x进行离散化,否则x为浮点数且很大无法进行线段树   

//以横坐标作为线段(区间),对横坐标线段进行扫描  
//扫描的作用是每次更新下底边总长度和下底边个数,增加新面积   
struct seg{//线段   
	double l, r, h;
	int d;
	seg(){}
	seg(double x1, double x2, double H, int c) :l(x1), r(x2), h(H), d(c){}
	bool operator<(const seg &a)const{
		return h<a.h;
	}
}s[MAX];

void Upfather(int rt, int left, int right){
	if (mark[rt])sum[rt] = Hash[right + 1] - Hash[left];//表示该区间整个线段长度可以作为底边   //注意right+1
	else if (left == right)sum[rt] = 0;//叶子结点则底边长度为0(区间内线段长度为0)   
	else sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void Update(int L, int R, int d, int rt, int left, int right){
	if (L <= left && right <= R){//该区间是当前扫描线段的一部分,则该区间下底边总长以及上下底边个数差更新   
		mark[rt] += d;//更新底边相差差个数   
		Upfather(rt, left, right);//更新底边长   
		return;
	}
	int mid = left + right >> 1;
	if (L <= mid)Update(L, R, d, rt << 1, left, mid);
	if (R>mid)Update(L, R, d, rt << 1 | 1, mid + 1, right);
	Upfather(rt, left, right);
}

int search(double key, double* x, int n){
	int left = 0, right = n - 1;
	while (left <= right){
		int mid = left + right >> 1;
		if (x[mid] == key)return mid;
		if (x[mid]>key)right = mid - 1;
		else left = mid + 1;
	}
	return -1;
}

int main(){
	int n, cas = 0;
	double x1, x2, y1, y2;
	while (cin >> n, n){
		int n_seg = 0;//n_seg为线段数
		for (int i = 0; i<n; ++i){
			cin >> x1 >> y1 >> x2 >> y2;
			Hash[n_seg] = x1;
			s[n_seg++] = seg(x1, x2, y1, 1);
			Hash[n_seg] = x2;
			s[n_seg++] = seg(x1, x2, y2, -1);
		}
		/*离散化x坐标(压缩成较小整数)*/
		sort(Hash, Hash + n_seg);//对x坐标点排序
		int n_p = 1;//n_p为不重复点数
		for (int i = 1; i<n_seg; ++i)//去重复端点   
		if (Hash[i] != Hash[i - 1])Hash[n_p++] = Hash[i];

		/*扫描线开始,从下往上扫*/
		sort(s, s + n_seg);//对线段排序
		double ans = 0;
		memset(mark, 0, sizeof mark);
		memset(sum, 0, sizeof sum);//如果下面是i<n_seg-1则要初始化,因为如果对第n_seg-1条线段扫描时会使得mark,sum为0才不用初始化的   
		for (int i = 0; i<n_seg-1; ++i){//扫描线段  
			int L = lower_bound(Hash, Hash + n_p, s[i].l) - Hash;
			int R = lower_bound(Hash, Hash + n_p, s[i].r) - Hash - 1;//注意-1
			//int L = search(s[i].l, Hash, n_p);
			//int R = search(s[i].r, Hash, n_p) - 1;//注意-1
			Update(L, R, s[i].d, 1, 0, n_p - 1);//扫描线段时更新底边长度和底边相差个数  
			ans += sum[1] * (s[i + 1].h - s[i].h);//新增加面积   
		}
		printf("Test case #%d\nTotal explored area: %.2lf\n\n", ++cas, ans);
	}
	return 0;
}
/*
这里注意下扫描线段时r-1:int R=search(s[i].l,Hash,m)-1;
计算底边长时r+1:if(mark[n])sum[n]=Hash[right+1]-Hash[left];
解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
则我们去更新的时候,会算到sum[1]=Hash[mid]-Hash[left]+Hash[right]-Hash[mid+1]
这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如Hash[3]=1.2,Hash[4]=5.6,mid=3
所以这里用r-1,r+1就很好理解了
*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值