hdu1255 覆盖的面积(扫描线-矩形面积交)

题目

N(N<=1e3)个矩形,每个矩形提供左下角(x1,y1)和右上角(x2,y2)的点

四个坐标均为浮点数,且在0到1e5之间

求被矩形覆盖过至少两次的区域的面积,结果保留两位小数

思路来源

http://cache.baiducontent.com/c?m=9d78d513d9d431a54f9d94697d1cc0171c4381132ba7d7020ed28449e3732a405011e7ac57520770a5d27c165bf90e4bea812172411420c698c88f4dc9fcd27620d26172325d914061ce04ea8e0332c157c106b9f144b2fbe732e4ff8f8cc22e44ca256521c4a7d7474209c16ef7103ae6ac8e48620556e5b3&p=c057c816d9c106b742bd9b7d0d108d&newp=882a9645d0815afc57ef8e2415558f231610db2151d7d601298ffe0cc4241a1a1a3aecbf23291202d9ce766c05ab4c5cecf03c70360234f1f689df08d2ecce7e6d93&user=baidu&fm=sc&query=%B8%B2%B8%C7%B5%C4%C3%E6%BB%FD+HDU+%2D+1255&qid=e54c0cad0002fb6e&p1=3

题解

其实就是cnt值的区别

当求矩形面积并的时候,cnt>0的答案就会计算在内

而算矩形面积交的时候,cnt>=2的答案才会被计算在内

注意cnt==1向cnt==2的转化,此时,父节点多了一条边而使得子节点的cnt==1的情形变为cnt==2的情形

所以每个节点内保存两个值,一个len2代表下面有两条矩形底边的长度,len1代表下面有一条矩形底边的长度

len1的判断和矩形面积并是一样的,len2在cnt>=2的时候也是一样的,只特判cnt==1的情形即可

核心在于pushup的写法,剩下的基本是一样的,

注意正确的写法,使得不存在没有意义的p<<1和p<<1|1

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2005;
int cas,n,now;
double ans;
double x[maxn];
double x1,y1,x2,y2;
struct node
{
	int l,r;
	double len2,len1;//被覆盖两次的长度,被覆盖一次的长度 
	int cnt;//当前有cnt条重叠的下线段 
}e[maxn*5];
struct edge
{
	double l,r,h;//当前线段的左界,右界,高度 
	int op;//矩形op==1是下线段,-1是上线段 
	edge(){}
	edge(double ll,double rr,double hh,int o):l(ll),r(rr),h(hh),op(o){}
	friend bool operator<(edge a,edge b){return a.h<b.h;}
}g[maxn];
void build(int p,int l,int r)
{
	e[p].l=l;e[p].r=r;
	e[p].len2=e[p].len1=0;
	e[p].cnt=0;
	if(l==r)return;
	int mid=(l+r)/2;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r); 
}
void pushup(int p)
{
	if(e[p].cnt>1)e[p].len2=e[p].len1=x[e[p].r+1]-x[e[p].l];//[l,r)的线段由[l,r-1]表示,但长度仍为[l,r]的长度 
	//当前父覆盖了1边 考虑让子的不完整1边变为不完整2边
	/*
	  形如 
	  -------- e[p].cnt++;
	  --    -- e[p<<1].cnt=e[p<<1|1].cnt=1
	*/
	else if(e[p].cnt==1) 
	{
		if(e[p].l==e[p].r)e[p].len2=0;
		else e[p].len2=e[p<<1].len1+e[p<<1|1].len1;
		e[p].len1=x[e[p].r+1]-x[e[p].l];
	} 
	else
	{
		if(e[p].l==e[p].r)e[p].len2=e[p].len1=0;
		else
		{
			e[p].len2=e[p<<1].len2+e[p<<1|1].len2;
			e[p].len1=e[p<<1].len1+e[p<<1|1].len1; 
		}
	} 
}
void update(int p,int ql,int qr,int op)
{
	if(ql<=e[p].l&&e[p].r<=qr)
	{
		e[p].cnt+=op;
		pushup(p);
		return;
	}
	int mid=(e[p].l+e[p].r)/2;
	if(ql<=mid)update(p<<1,ql,qr,op);
	if(qr>mid)update(p<<1|1,ql,qr,op);
	pushup(p);
}
int main()
{
	scanf("%d",&cas);
	while(cas--)
	{
	    scanf("%d",&n); 
		for(int i=1;i<=n;++i)//1到2*n
		{
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			x[2*i-1]=x1;
			x[2*i]=x2;
			g[2*i-1]=edge(x1,x2,y1,1);
			g[2*i]=edge(x1,x2,y2,-1); 
		}
		sort(x+1,x+2*n+1);
		now=1;
		for(int i=2;i<=2*n;++i)//强制去重x数组 
		if(x[i]!=x[now])x[++now]=x[i];
		build(1,1,now);
		sort(g+1,g+2*n+1);
		ans=0;
		for(int i=1;i<=2*n-1;++i)
		{
			int l=lower_bound(x+1,x+now+1,g[i].l)-x;
			int r=lower_bound(x+1,x+now+1,g[i].r)-x;
			//[l,l+1)代表线段l,边权线段比点少一个 3个点2条边 
			//[l,r)内有线段r-l条,故用[l,r-1]来代替
			//每个线段对应一个标号 
			update(1,l,r-1,g[i].op); 
			//printf("[%d]:%lf [%d]:%lf\n",i+1,g[i+1].h,i,g[i].h);
			//printf("e[1].len2:%lf\n",e[1].len2);
			ans+=(g[i+1].h-g[i].h)*e[1].len2;
		} 
		printf("%.2lf\n",ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值