hdu 1255 线段树的面积覆盖问题

/*

以前没想到线段树还能这样用,阅读了胡浩大牛的解题报告,这个线段重叠的思想很巧妙,先把一个矩形拆成两条线段,记录每条线段的两个端点及它的高度,并标记此线段是在矩形的上边还是下边,在利用cnt[]数组记录这个x轴的区间段被这样的线段覆盖了几次,那么在这一题来说,覆盖两次以上既是我们要求的解,所以我们在节点上只要保存覆盖一次,和覆盖两次及两次以上的x长度就可以了,最后用所有x轴被覆盖的长度也就是树根的值乘以y轴的区间(也就是两线段的差值),在累加求和就是解了,对于y轴就相当于枚举了,只不过先要对线段的高度排一下序,这样取y轴区间时便于操作;

自己写了一下,代码如下:

*/

#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;
const int N=10000;

struct tree{double n1,n2;} sum[N<<2];
struct seg{ 
	double x1,x2,h,v;
	bool operator <(const seg &tem) const
	{
		return h<tem.h;
	}
} line[N];

double cnt[N<<2],X[N];

void Set(int k,double x1,double y1,double x2,double y2)
{
	X[k]=x1;
	X[k+1]=x2;
	line[k].x1=x1;line[k].x2=x2;line[k].h=y1;line[k].v=1;
	line[k+1].x1=x1;line[k+1].x2=x2;line[k+1].h=y2;line[k+1].v=-1;
}
void Pushup(int i,int l,int r)
{
	if(cnt[i]>=2){sum[i].n2=sum[i].n1=X[r+1]-X[l];}
	else if(cnt[i]==1)
	{
		sum[i].n1=X[r+1]-X[l];
		if(l==r)sum[i].n2=0;
		else sum[i].n2=sum[i<<1].n1+sum[i<<1|1].n1;
	}
	else 
	{
		if(l==r)sum[i].n1=sum[i].n2=0;
		else 
		{
			sum[i].n1=sum[i<<1].n1+sum[i<<1|1].n1;
			sum[i].n2=sum[i<<1].n2+sum[i<<1|1].n2;
		}
	}
}
void update(int i,int l,int r,int a,int b,int v)
{
	if(l==a&&r==b)
	{
		cnt[i]+=v;
		Pushup(i,l,r);
		return ;
	}
	int mid=(l+r)>>1;
	if(b<=mid)update(i<<1,l,mid,a,b,v);
	else if(b>mid&&a<=mid)
	{
		update(i<<1,l,mid,a,mid,v);
		update(i<<1|1,mid+1,r,mid+1,b,v);
	}
	else update(i<<1|1,mid+1,r,a,b,v);
	Pushup(i,l,r);
}

int Bin(int l,int r,double v)
{
	int mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(X[mid]==v)return mid;
		else if(X[mid]>v) r=mid-1;
		else l=mid+1;
	}
	return -1;
}
int main()
{
	int cas,n,k,m,i;
	double x1,x2,y1,y2,ans;
	scanf("%d",&cas);
	while(cas--)
	{
		m=0;
		scanf("%d",&n);
		while(n--)
		{
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			Set(m,x1,y1,x2,y2);
			m+=2;
		}
		sort(X,X+m);
		sort(line,line+m);
		for(i=k=1;i<m;i++)
			if(X[i-1]<X[i])
				X[k++]=X[i];
		int l,r;
		ans=0;
		memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
		for(i=0;i<m-1;i++)
		{
			l=Bin(0,k-1,line[i].x1);
			r=Bin(0,k-1,line[i].x2)-1;
			if(l<=r)update(1,0,k-1,l,r,line[i].v);
			ans+=sum[1].n2*(line[i+1].h-line[i].h);
		}
		printf("%.2lf\n",ans);
	}
	return 0;
}

/*
10
2
0 0 2 2
1 1 3 3
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值