hdu3340(线段树+几何)


给定N个多边形,然后每次查询一段坐标内图形的面积。


我们可以将多边形拆解为多个梯形,转化为每一次在一个区间加一个等差数列,注意这个等差数列是实数的,其实就是一个区间加一个梯形面积,而梯形面积我们只需要知道左右端点的高就好,这样打标记,累加左右的高,是可以维护的。


这里线段树是要离散化的,所以下传标记找mid高度时,要注意不是线段树区间的中点,而是实际区间的中点,

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=125005;

struct point{int x,y;};
int n;
int b[N],nn;

struct aa
{
	int l,r;
	double lh,rh,len,sum;
}a[N*4];
void build(int i,int l,int r)
{
	a[i].len=b[r+1]-b[l];
	a[i].sum=a[i].lh=a[i].rh=0;
	a[i].l=l,a[i].r=r;
	if (l==r) return ;
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
}
void up(int i) {a[i].sum=a[i<<1].sum+a[i<<1|1].sum;}
void down(int i)
{
	int l=i<<1,r=i<<1|1;
	if (a[i].lh!=0||a[i].rh!=0) 
	{
		double midh=a[i].lh+(a[i].rh-a[i].lh)*(a[l].len/a[i].len);
		a[l].sum+=(a[i].lh+midh)*a[l].len/2;
		a[r].sum+=(midh+a[i].rh)*a[r].len/2;
		a[l].lh+=a[i].lh,a[l].rh+=midh;
		a[r].lh+=midh,a[r].rh+=a[i].rh;
		
		a[i].lh=a[i].rh=0;
	}
}
void add(int i,int l,int r,double lh,double rh) 
{
	if (a[i].l==l&&a[i].r==r) 
	{
		a[i].sum+=a[i].len*(lh+rh)/2;
		a[i].lh+=lh,a[i].rh+=rh;
		return ;
	}
	down(i);
	int mid=(a[i].l+a[i].r)>>1;
	if (r<=mid) add(i<<1,l,r,lh,rh);
	else if (mid<l) add(i<<1|1,l,r,lh,rh);
	else 
	{
		double midh=lh+(rh-lh)*(double(b[mid+1]-b[l])/double(b[r+1]-b[l]));
		add(i<<1,l,mid,lh,midh);
		add(i<<1|1,mid+1,r,midh,rh);
	}
	up(i);
} 
double query(int i,int l,int r) 
{
	if (a[i].l==l&&a[i].r==r) return a[i].sum;
	down(i);
	int mid=(a[i].l+a[i].r)>>1;
	if (r<=mid) return query(i<<1,l,r);
	else if (mid<l) return query(i<<1|1,l,r);
	return query(i<<1,l,mid)+query(i<<1|1,mid+1,r);
}

struct QUE
{
	int op,num,l,r;
	point p[5];
}Q[25005];

inline int pos(int x) {return lower_bound(b+1,b+nn+1,x)-b;}
void work()
{
	scanf("%d",&n);
	char ch[2];
	nn=0;
	for (int i=1;i<=n;i++) 
	{
		scanf("%s",ch);
		if (ch[0]=='R') 
		{
			Q[i].op=1;
			scanf("%d",&Q[i].num);
			for (int j=0;j<Q[i].num;j++) scanf("%d%d",&Q[i].p[j].x,&Q[i].p[j].y),b[++nn]=Q[i].p[j].x;
		}
		else Q[i].op=2,scanf("%d%d",&Q[i].l,&Q[i].r),b[++nn]=Q[i].l,b[++nn]=Q[i].r;
	}
	sort(b+1,b+nn+1);
	nn=unique(b+1,b+nn+1)-b-1;
	build(1,1,nn-1);
	
	for (int i=1;i<=n;i++) 
	{
		QUE &t=Q[i];
		if (t.op==1)
		{
			for (int r,l=0;l<t.num;l++)
			{
				r=(l+1)%t.num;
				if (t.p[r].x>t.p[l].x) add(1,pos(t.p[l].x),pos(t.p[r].x)-1,-t.p[l].y,-t.p[r].y);
				else add(1,pos(t.p[r].x),pos(t.p[l].x)-1,t.p[r].y,t.p[l].y);
			}
		}
		else printf("%.3lf\n",query(1,pos(t.l),pos(t.r)-1));
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	while (T--) work();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值