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

Atlantis

题目链接:HDU - 1542

题意:求n个矩形合成图形的面积,如下图是由三个矩形构成,求出所涂颜色区域的面积;

当图形很少时可以暴力求解,S=S红+S紫+S蓝;但是这样是很麻烦的,怎样让他简单化呢?

观察一下上图的墨绿线,这些线很好的将原图划分为五部分,面积就是这五部分和,这就是扫描线;

我们可以把墨绿线看作是由左向右移动的,当他碰到一条边的时候暂停一下,那么第一部分的面积一定是该处的边的长度L乘下一条边距离此处的距离D,然后继续向右移动,遇到第二条边再停止,计算第二部分面积,...  ...  ,知道移动到最后一条边处,停止计算;

D很好算,把所有边排序后,直接后减前;但是L是由多个边构成的不好算;这里就用到了线段树了,用线段树记录并合并区间长度;

通常线段树是这样的:

每个结点存的但单点,而线段是连续的;如果线段[1, 3]存入线段树,会直接存入1-2和3两个节点,计算其长度时是2-1+3-3=2,就造成了信息缺失现象,所以对于线段[l, r]我们应该 以[l, r-1]的形式存入,计算长度时为l+1-r;

#include <bits/stdc++.h>
using namespace std;
struct Line{//存边
	double x, y1, y2;
	int v;//记录该边时矩形的起始边还是终止边,前者1后者-1;
	Line(){}
	Line(double x0, double y10, double y20, int v0){
		x=x0, y1=y10, y2=y20, v=v0;
	}
	bool operator < (const Line &p) const{
		return x<p.x;
	}
}line[300];
double y[300];
int cnt_y, cnt_l;
struct node{
	int l, r, v;//v表示[l, r]区间是否被覆盖;
	double len;//[l, r]区间被覆盖长度;
}tr[1000];
void build(int m, int l, int r){
	tr[m].l=l;
	tr[m].r=r;
	tr[m].v=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(m<<1, l, mid);
	build(m<<1|1, mid+1, r);
}
void pushup(int m){//计算区间被覆盖长度;
	if(tr[m].v) tr[m].len=y[tr[m].r+1]-y[tr[m].l];//整个区间被覆盖时,长度是整个区间长度;
	else if(tr[m].l==tr[m].r) tr[m].len=0;//叶节点为0;
	else tr[m].len=tr[m<<1].len+tr[m<<1|1].len;//为被全部覆盖时,长度为子区间被覆盖长度和;
}
void updata(int m, int l, int r, int v){
	if(tr[m].l==l&&tr[m].r==r){
		tr[m].v+=v;
		pushup(m);
		return;
	}
	int mid=(tr[m].l+tr[m].r)>>1;
	if(r<=mid) updata(m<<1, l, r, v);
	else if(l>mid) updata(m<<1|1, l, r, v);
	else{
		updata(m<<1, l, mid, v);
		updata(m<<1|1, mid+1, r, v);
	}
	pushup(m);
}
int main(){
	int n, cas=0;
	double x1, x2, y1, y2;
	while(scanf("%d", &n), n){
		cas++;
		cnt_l=cnt_y=0;
		for(int i=0; i<n; i++){
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			y[++cnt_y]=y1;
			y[++cnt_y]=y2;
			line[++cnt_l]=Line(x1, y1, y2, 1);
			line[++cnt_l]=Line(x2, y1, y2, -1);
		}
		sort(line+1, line+1+cnt_l);
		sort(y+1, y+1+cnt_y);
		cnt_y=unique(y+1, y+1+cnt_y)-(y+1);//去重点,离散y;
		build(1, 1, cnt_y);
		double ans=0.0;
		for(int i=1; i<=cnt_l; i++){
			int l=upper_bound(y+1, y+1+cnt_y, line[i].y1)-(y+1);
			int r=upper_bound(y+1, y+1+cnt_y, line[i].y2)-(y+1)-1;//存入区间是[l, r-1];
			updata(1, l, r, line[i].v);
			ans+=tr[1].len*(line[i+1].x-line[i].x);
		}
		printf("Test case #%d\nTotal explored area: %.2f\n\n", cas, ans);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值