HDU1255 覆盖的面积 (线段树求面积交)

覆盖的面积

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6242    Accepted Submission(s): 3149


Problem Description
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.


 

Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.

注意:本题的输入数据较多,推荐使用scanf读入数据.
 

Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
 

Sample Input
  
  
2 5 1 1 4 2 1 3 3 7 2 1.5 5 4.5 3.5 1.25 7.5 4 6 3 10 7 3 0 0 1 1 1 0 2 1 2 0 3 1
 

Sample Output
  
  
7.63 0.00
 

Author
Ignatius.L & weigang Lee


思路:定义一个变量cnt,表示该区间是否被完全覆盖,s表示区间覆盖1次或以上的长度,ss表示区间覆盖2次及以上的长度,然后分类讨论:

          cnt > 1 表示该区间被覆盖2次及以上,则该区间完全被覆盖,区间的长度则为覆盖的长度,其他的情况则就是cnt==1和cnt==0或者l == r

  cnt = 1 表示该区间被覆盖1次或以上,则该区间的长度等于其左区间覆盖一次或以上的长度+右区间覆盖一次或以上的。

          cnt = 0 则该区间的长度等于其左区间覆盖两次或以上+右区间覆盖两次或以上的长度

附一大佬博客链接:http://www.cnblogs.com/scau20110726/archive/2013/04/14/3020998.html


AC代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
const int maxn = 1010;
struct LINE{
	double l,r,h;//l,r表示某条线段的左右端点的坐标,h表示高度
	int flag; //flag=1表示下边,flag=-1表示上边
}line[maxn << 3];
struct node{
	int l,r,cnt; //l,r表示以rt为根节点的左右区间,cnt记录当前重叠几次
	double s,ss;//s表示重叠1次及以上的长度,ss表示重叠2次及以上的长度
}p[maxn << 3];  //该范围至少要开*8的空间
int n,t;
double x[maxn << 3];

bool cmp(LINE A, LINE B){
	return A.h < B.h;
}

void build(int l, int r, int rt){
	p[rt].l = l; p[rt].r = r;
	p[rt].cnt = p[rt].s = p[rt].ss = 0;
	if(l == r) return ;
	int m = (l + r) >> 1;
	build(l,m,ls);
	build(m+1,r,rs);
}

void pushup(int rt){
	if(p[rt].cnt) p[rt].s = x[p[rt].r + 1] - x[p[rt].l];
	else if(p[rt].l == p[rt].r) p[rt].s = 0;
	else p[rt].s = p[ls].s + p[rs].s;
    //面积覆盖两次及以上,则等于该区间内的线段长,左端点=右端点则线段长度为0,面积覆盖1次或以上,则等于左右区间的和(保证两次及以上)
    //面积覆盖0次或以上,则等于左右覆盖两次或以上的区间和
	if(p[rt].cnt > 1) p[rt].ss = x[p[rt].r + 1] - x[p[rt].l];
	else if(p[rt].r == p[rt].l) p[rt].ss = 0;
	else if(p[rt].cnt == 1) p[rt].ss = p[ls].s + p[rs].s;
	else p[rt].ss = p[ls].ss + p[rs].ss;
}

void update(int l, int r, int rt, int flag){
	if(p[rt].l == l && p[rt].r == r){//找到该线段
		p[rt].cnt += flag;
		pushup(rt);
		return;
	}
	int m = (p[rt].l + p[rt].r) >> 1;
	if(r <= m) update(l,r,ls,flag);
	else if(l > m) update(l,r,rs,flag);
	else{
		update(l,m,ls,flag);
		update(m+1,r,rs,flag);
	}
	pushup(rt);
}

int main(){
	scanf("%d",&t);
	while(t --){
		scanf("%d",&n);
		double x1,x2,y1,y2;
		int tot = 0;
		for(int i = 0; i < n; i ++){
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			x[tot] = x1; x[tot+1] = x2;
			line[tot].h = y1; line[tot+1].h = y2;
			line[tot].flag = 1; line[tot+1].flag = -1;
			line[tot].l = line[tot+1].l = x1;
			line[tot].r = line[tot+1].r = x2;
			tot += 2;
		}
		sort(x,x+tot);
		sort(line,line+tot,cmp);
		int k = 1;
		for(int i = 1; i < tot; i ++){ //离散化,去掉重复的坐标
			if(x[i] != x[i-1]) x[k ++] = x[i];
		}
		build(0,k-1,1);
		double ans = 0;
		for(int i = 0; i < tot-1; i ++){
			int l = lower_bound(x,x+k,line[i].l) - x;
			int r = lower_bound(x,x+k,line[i].r) - x - 1;
			update(l,r,1,line[i].flag);
			ans += (line[i+1].h - line[i].h) * p[1].ss;
		}
		printf("%.2lf\n",ans);
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值