线段树 矩形并

http://www.cnblogs.com/ka200812/archive/2011/11/13/2247064.html

给定一个矩形的左下角坐标和右上角坐标分别为:(x1,y1)、(x2,y2),对这样的一个矩形,我们构造两条线段,一条定位在x1,它在y坐标的区间是[y1,y2],并且给定一个cover域值为1;另一条线段定位在x2,区间一样是[y1,y2],给定它一个cover值为-1。根据这样的方法对每个矩形都构造两个线段,最后将所有的线段根据所定位的x从左到右进行排序。

上图中,红色的字体表示的是该线段的cover值。刚刚开始的时候,线段树上的cover值都为0,但第一根线段(x==0)插入线段树的之后,我们将线段树上的cover加上该线段的cover,那么,此时线段树上被该线段覆盖的位置上的cover的值就为1,下次再插入第二根线段(x==1)此时发现该线段所覆盖的区间内,有一部分线段树的cover为0,另有一部分为1,仔细观察,但插入第二个线段的时候,如果线段树上cover已经为1的那些区间,和现在要插入的第二根线段之间,是不是构成了并面积?还不明白?看下图,绿色部分即为插入第二根线段后得到的并面积



在插入第三条线段时,计算的是1,2,3,4块的面积,在插入第四条线段时,计算5的面积

够清楚了吧!也就是说,我们插入某跟线段的时候,只要看该线段所在区间上的cover是否大于等于1,如果是,那么就可以将并面积值加上(目前线段的x定位 - 上一线段的x定位)*(该区间的大小)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define  lson(i) (idx << 1)
#define  rson(i) ((idx << 1) ^ 1)
using namespace std;

const int N = 1010;
double y[N];
struct segment{
	double x, y1, y2;
	int flag;
	segment(){}
	segment(const double x, const double y1, const double y2, const int flag){
		this->x = x, this->y1 = y1, this->y2 = y2, this->flag = flag; 	
	}
	bool operator < (const segment &a) const {
		return x < a.x;
	}
}line[N];
struct segTree{
	int lc, rc, cover;
	double up, down, len;
}arr[N * 4];

void pushUp(int idx){
	if(arr[idx].cover > 0)	
		arr[idx].len = arr[idx].up - arr[idx].down;
 	else 
		arr[idx].len = arr[lson(idx)].len + arr[rson(idx)].len;
}

void build(int idx, int l, int r){
	arr[idx].lc = l, arr[idx].rc = r;
	arr[idx].up = y[r], arr[idx].down = y[l];
	arr[idx].len = arr[idx].cover = 0;
	if(r - l == 1)
		return ;
	int mid = (l + r) >> 1;
	/**
	这里和普通的线段树有点不同,不能以左区间[1,mid],右区间[mid+1,r]来建树
	如果这样建会漏掉一些线段,比如10,20,30,40这些点来建树,按普通的建法是
	[10,20], [30,40],这样[20,30]这跟线段就漏掉了 
	所以需要左区间[l,mid],右区间[mid,r]这样来建;建出来是[10,20],[20,30],[30,40]
	这样就没线段漏掉了.
	**/
	build(lson(idx), l, mid);
	build(rson(idx), mid, r);
}

void update(int idx, int l, int r, int val){
	if(arr[idx].lc > r || l > arr[idx].rc)
		return ;
	if(arr[idx].lc >= l && arr[idx].rc <= r){
		arr[idx].cover += val;
		pushUp(idx);
		return ;		
	}
	update(lson(idx), l, r, val);
	update(rson(idx), l, r, val);
	pushUp(idx);
}

int main(){
	int n, no = 1;
	while(cin >> n, n){
		int i, idx = 1;
		double x1, x2, y1, y2;
		for(i = 1;i <= n;i++){
			cin >> x1 >> y1 >> x2 >> y2;
			line[idx] = segment(x1, y1, y2, 1);
			y[idx++] = y1;
			
			line[idx] = segment(x2, y1, y2, -1);
			y[idx++] = y2;
		}
		sort(y + 1, y + idx);
		sort(line + 1, line + idx);
		/**以y轴建树**/ 
		build(1, 1, idx - 1);
		double res = 0;
		for(i = 1;i < idx;i++){
			res += arr[1].len * (line[i].x - line[i - 1].x);
			/**二分出当前扫描线y1,y2的位置**/
			int fir = lower_bound(y + 1, y + idx, line[i].y1) - y;
			int sec = lower_bound(y + 1, y + idx, line[i].y2) - y;
			update(1, fir, sec, line[i].flag);
		}
		printf("Test case #%d\nTotal explored area: %.2f\n\n", no++, res);							
	}
	return 0;	
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值