HDU_1542_扫描线求面积(线段树维护)

HDU 1542

  • 一条竖直直线从左到右(反向也行)扫描,那么将扫描出来的直线的y坐标离散化,然后用线段树维护y轴的覆盖区间.x值作为高度
  • 如果一条横着直线从下到上(反向也行)扫描,将扫描出来的x坐标离散化,线段树维护x轴的覆盖区间,y作为高度.
  • 此题从上到下扫,将x坐标离散化,建树
  • 将一个矩形的2条边标记为上边1和下边-1,用线段树维护时不需要延迟标记,因为成对出现,下一次对应边出现就会消去此次覆盖.
  • 但是线段树端点出问题,一条上边[1,3],一条下边[3,5],3最后覆盖值为0,就会少算一个覆盖点,方法:线段树的右端都-1变成[1,2] [3,4],
  • 线段树节点维护2个信息,sum[]:覆盖长度,add[]:节点被覆盖的次数,
  1. add>0,该节点的sum = x[right+1] - x[left]
  2. add = 0,该节点没有被覆盖,那么覆盖区间是子节点的覆盖区间:sum=sum[2*p]+sum[2*p+1]
#include <iostream>
#include <string.h>
#include <algorithm>
#define lson p<<1,left,mid
#define rson p<<1|1,mid+1,right
using namespace std;
const int maxn = 2000+5;

int add[maxn << 2];
double x[maxn<<2],sum[maxn<<2];
int n, cnt, res;

struct node {
	double l, r, h;
	int s;
	node() {};
	node(double ll,double rr,double hh,int ss) {
		l = ll; r = rr; h = hh; s = ss;
	}
	bool operator<(const node& temp)const {
		return h < temp.h;
	}
}s[maxn];
void pushup(int p,int left,int right) {
	if (add[p]) sum[p] = x[right + 1] - x[left]; //得到长度
	else if (left == right) sum[p] = 0; //[x[right+1],x[left]],由于没有覆盖所以=0
	else sum[p] = sum[p << 1] + sum[p<<1|1];//向上更新长度
}
void updata(int L,int R,int val,int p,int left,int right) {
	if (L <= left && right <= R) {
		add[p] += val;
		pushup(p,left,right);
		return;
	}
	int mid = (left + right) >>1;
	if (L <= mid) updata(L,R,val,lson);
	if (mid < R) updata(L,R,val,rson);
	pushup(p,left,right);
}
//int binary_find(double val) {
//	int left = -1, right = res - 1;
//	while (right - left > 1) {
//		int mid = (left + right)>>1;
//		if (x[mid] >= val) right = mid;
//		else left = mid;
//	}
//	return right;
//}
int binary_find(double val) {
	int left = 0, right = res - 1;
	while (left <= right) {
		int mid = (left + right) >> 1;
		if (x[mid] < val) left = mid + 1;
		else right = mid - 1;
	}
	return left;
}
int main() {
	int count = 1; //freopen("a.txt","r",stdin);
	while (scanf("%d",&n) != EOF && n) {
		cnt = 0;
		for (int i = 0; i < n; i++) {
			double a, b, c, d;
			scanf("%lf%lf%lf%lf",&a,&b,&c,&d); //a,b是左上角的点, c,d,是右上角的点
			s[cnt] = node(a,c,b,1);
			x[cnt++] = a; //横坐标存下来,进行离散化
			s[cnt] = node(a,c,d,-1);
			x[cnt++] = c;
		}
		sort(s,s+cnt); //扫描线按高度排序
		sort(x,x+cnt); //离散化的坐标进行排序
		res=1; //去重的辅助标记
		for (int i = 1; i < cnt; i++)
			if (x[i] != x[i - 1]) x[res++] = x[i];//将相同的去重,移到前面去
		memset(add,0,sizeof add);
		memset(sum,0,sizeof sum);
		double ans = 0;
		for (int i = 0; i < cnt-1; i++) {
			int l = binary_find(s[i].l);
			int r = binary_find(s[i].r) - 1; //在x中找到离散后的坐标
			updata(l,r,s[i].s,1,0,res-1); //res是去重后坐标的总数+1
			ans += sum[1] * (s[i+1].h - s[i].h);
		}
		printf("Test case #%d\nTotal explored area: %.2lf\n\n", count++, ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值