HDU 1255 覆盖的面积 (线段树扫描线)

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

思路:线段树扫描线。

之前写过求矩形并的面积,求至少覆盖两次的面积只需要对线段树进行小小的修改。

线段树节点定义如下:

<span style="font-size:14px;">struct Node{
	int Cover;//覆盖次数
	double CoverL[3];//CoverL[i]=覆盖>=i次的长度 
};</span>
然后更新节点的过程,以CoverL[2]为例:

如果Cover==0 则CoverL[2]利用子树的CoverL[2]来更新

如果Cover==1 则CoverL[2]利用子树的CoverL[1]来更新

如果Cover==2 则CoverL[2]利用子树的CoverL[0]来更新

其中CoverL[0]就是每个节点的总长度。


代码如下:

//218MS 1816K  
#include <iostream>
#include <cstdio>
#include <algorithm>
#define eps 1e-9
#define maxn 2007 
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
int sgn(double x){return (x>-eps)-(x<eps);}
//记录线段 
struct Lines{
	double x,y1,y2;
	bool IN;
	Lines(){}
	Lines(double x,double y1,double y2,bool IN):x(x),y1(y1),y2(y2),IN(IN){}
	bool operator <(const Lines &B)const{return sgn(x-B.x)<0;}
}L[maxn];
//离散化 
int n,T,N,Rn;
double Rank[maxn]; 
int GetRank(double x){
	int L=0,R=Rn;//[L,R] first >= x
	while(L^R){
		int M=(L+R)>>1;
		if(Rank[M]<x) L=M+1;
		else R=M;
	}
	return L;
}
//线段树 
struct Node{
	int Cover;//覆盖次数
	double CoverL[3];//CoverL[i]=覆盖>=i次的长度 
}K[maxn<<2];
void PushUp(int rt){
	int X;
	if(K[rt].Cover<=1){
		X=1-K[rt].Cover;
		K[rt].CoverL[1]=K[rt<<1].CoverL[X]+K[rt<<1|1].CoverL[X];
	}
	else K[rt].CoverL[1]=K[rt].CoverL[0];
	if(K[rt].Cover<=2){
		X=2-K[rt].Cover;
		K[rt].CoverL[2]=K[rt<<1].CoverL[X]+K[rt<<1|1].CoverL[X];
	}
	else K[rt].CoverL[2]=K[rt].CoverL[0];
}
void Build(int l,int r,int rt){
	if(l==r){
		K[rt].Cover=0;
		K[rt].CoverL[0]=Rank[l]-Rank[l-1];
		K[rt].CoverL[1]=K[rt].CoverL[2]=0.0;
		return;
	}
	int m=(l+r)>>1;
	Build(ls);
	Build(rs);
	K[rt].Cover=0;
	K[rt].CoverL[0]=K[rt<<1].CoverL[0]+K[rt<<1|1].CoverL[0];
	K[rt].CoverL[1]=K[rt].CoverL[2]=0.0;
}
void Update(int L,int R,int C,int l,int r,int rt){
	if(L <= l && r <= R){
		K[rt].Cover+=C;
		if(l==r){
			K[rt].CoverL[1]=K[rt].Cover >= 1 ? K[rt].CoverL[0] : 0.0;
			K[rt].CoverL[2]=K[rt].Cover >= 2 ? K[rt].CoverL[0] : 0.0;
		}
		else PushUp(rt);
		return;
	}
	int m=(l+r)>>1;
	if(L <= m) Update(L,R,C,ls);
	if(R >  m) Update(L,R,C,rs);
	PushUp(rt);
}
int main(void)
{
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);N=n<<1;
		for(int i=0;i<n;++i){
			double x1,y1,x2,y2;
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			Rank[i<<1]=y1;
			Rank[i<<1|1]=y2;
			L[i<<1]=Lines(x1,y1,y2,true);
			L[i<<1|1]=Lines(x2,y1,y2,false);
		}
		//线段排序 
		sort(L,L+N);
		//离散化 
		sort(Rank,Rank+N);Rn=0;
		for(int i=1;i<N;++i){
			if(sgn(Rank[i]-Rank[i-1])) Rank[++Rn]=Rank[i];
		}
		//建立线段树 
		Build(1,Rn,1);
		//开始扫描 
		double X,PreX=L[0].x,PreL2=0.0,AREA2=0.0;
		int I=0;
		while(I<N){
			X=L[I].x;
			AREA2+=(L[I].x-PreX)*PreL2;//累计面积 
			while(I<N&&!sgn(L[I].x-X)){//更新线段树 
				Update(GetRank(L[I].y1)+1,GetRank(L[I].y2),L[I].IN?1:-1,1,Rn,1);
				++I;
			} 
			PreX=X;
			PreL2=K[1].CoverL[2];
		} 
		printf("%.2f\n",AREA2);
	}
return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值