梯形剖分入门(特殊情况)【ice】

7 篇文章 0 订阅
1 篇文章 0 订阅

传说把一个多边形按照顺时针(或者逆时针)旋转,相邻两个点分别作x轴的垂线(并且这两个点相连),就构成了梯形,然后定义从点在x轴方向上左到右为正(或者为负),得到n个梯形,把面积加起来就构成了多边形面积,这种剖分就是梯形剖分.


这种梯形剖分就把难以处理的多边形就面积转为求梯形面积(带有正负).


而最简单的情况是给你一些梯形求面积(相交也要重复计算),而且是特殊的直角梯形,例如下面这道题:

ICE

Description

艾莎女王又开始用冰雪魔法盖宫殿了。


她决定先造一堵墙,于是释放魔法让形为直角梯形的冰砖从天而降,定入冻土之中。


现在你将回答女王的询问:某段冻土上冰砖的面积。


注:多块冰砖之间会互相重叠,重叠部分要多次计算。




Input
第一行一个整数n,表示有n个冰砖先后定入了冻土之中。
冻土上刚开始并没有冰砖。
接下来n行,每行6个整数,x1i,h1i,x2i,h2i,li,ri。
表示一次如图所示的冰砖下落,并询问在这之后,落在[li,ri]内冰砖的总面积。
2≤n≤100000,?1e8≤li<ri≤1e8,?1e8≤x1i<x2i≤1e8,0≤h1i,h2i≤10000,x2i?x1i≤1e5


Output
输出n行,每行输出一个浮点数,作为对该询问的回答。误差小于1e-6的回答都被当作正确回答。


Sample input
2
1 1 3 2 -5 5
2 2 4 1 2 3


Sample Output
3.0000000

3.50000000


就是用线段树维护这区间的梯形


对于这种题我们维护的每一个点都是[l,r) 这样的区间,之前我还不懂,维护了[l,r]的区间,发现更新高度的时候,面积始终得不到更新,就出现了bug,于是蒟蒻的看了标程。


本题特殊情况,面积要重复计算

首先,梯形的面积公式是(lh+rh)*d/2           我们先在[l,r]加入一个梯形,得到S1=(lh1+rh1)*d/2

然后再加入一个梯形 得到S2=(lh2+rh2)*d/2           总面积是S=S1+S2=(lh1+rh1+lh2+rh2)*d/2

也可以看成是  合成一个新的梯形,lh=lh1+lh2,rh-rh1+rh2    S=(lh+rh)*d/2; 


具体的思想是:把lh,rh当成标记打在每个树的节点上。   查询的时候,二分这个区间,这个区间有lh,rh的标记,把(lh+rh)/2当成左区间的右端点和右区间的左端点的高就行了。  

本题需要离散化,所以在下放的时候需要计算一下离散后mid位置的高。


切记维护的区间是左闭右开的,其他的注意细节即可!


蒟蒻的代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const double eps=1e-10;
const int maxn=100000+5;
int n;
int x1[maxn],x2[maxn],h1[maxn],h2[maxn],l[maxn],r[maxn];
int det[4*maxn];
int tot;
int m;
int find(int x)
{
	return lower_bound(det+1,det+m+1,x)-det;
}
struct node
{
	int l,r;
	double lh,rh,sum;
}T[4*maxn*4];
void pushup(int i)
{
	T[i].sum=T[i<<1].sum+T[i<<1|1].sum;
}
void pushdown(int i)
{
	if(T[i].lh>eps||T[i].rh>eps)
	{
		int l=T[i].l;
		int r=T[i].r;
		int mid=(l+r)>>1;
		int xl=det[l];
		int xr=det[r];
		int xm=det[mid];
		double mm;
		mm=(T[i].rh*xm-T[i].lh*xm-T[i].rh*xl+T[i].lh*xr)/(xr-xl);
		
		T[i<<1].sum+=(T[i].lh+mm)*(xm-xl)/2;
		T[i<<1|1].sum+=(T[i].rh+mm)*(xr-xm)/2;
		
		T[i<<1].lh+=T[i].lh;
		T[i<<1|1].rh+=T[i].rh;	
		T[i<<1].rh+=mm;
		T[i<<1|1].lh+=mm;
		
		
		T[i].lh=T[i].rh=0;
	}
}
void build(int i,int l,int r)
{
	T[i].l=l;
	T[i].r=r;
	if(l+1==r)
	{
		T[i].lh=T[i].rh=T[i].sum=0;
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid,r);
	pushup(i);
}
void updata(int i,int L,int R,double lh,double rh)
{
	int l=T[i].l;
	int r=T[i].r;
	if(l==L&&r==R)
	{
		int xl=det[l],xr=det[r];
		T[i].lh+=lh;
		T[i].rh+=rh;
		T[i].sum+=(lh+rh)*(xr-xl)/2;
	}
	else
	{
		pushdown(i);
		int mid=(l+r)>>1;
		if(R<=mid)updata(i<<1,L,R,lh,rh);
		else if(L>=mid)updata(i<<1|1,L,R,lh,rh);
		else
		{
			int xl=det[L],xr=det[R],xm=det[mid];
			double mm=(rh*xm-lh*xm-rh*xl+lh*xr)/(xr-xl);
			updata(i<<1,L,mid,lh,mm);
			updata(i<<1|1,mid,R,mm,rh);
		}
		pushup(i);
	}
}
double query(int i,int L,int R)
{
	int l=T[i].l;
	int r=T[i].r;
	if(l==L&&r==R)return T[i].sum;
	pushdown(i);
	int mid=(l+r)>>1;
	double ans=0;
	if(R<=mid)ans+=query(i<<1,L,R);
	else if(L>=mid)ans+=query(i<<1|1,L,R);
	else ans=query(i<<1,L,mid)+query(i<<1|1,mid,R);
	pushup(i);
	return ans;
}
int main()
{
	freopen("ice.in","r",stdin);
	freopen("ice.out","w",stdout);
	scanf("%d",&n);
	tot=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d%d%d",&x1[i],&h1[i],&x2[i],&h2[i],&l[i],&r[i]);
		det[++tot]=x1[i];
		det[++tot]=x2[i];
		det[++tot]=l[i];
		det[++tot]=r[i];
	}
	sort(det+1,det+tot+1);
	m=unique(det+1,det+tot+1)-det-1;
	for(int i=1;i<=n;i++)
	{
		x1[i]=find(x1[i]);
		x2[i]=find(x2[i]);
		l[i]=find(l[i]);
		r[i]=find(r[i]);
	}
	build(1,1,m);
	for(int i=1;i<=n;i++)
	{
		updata(1,x1[i],x2[i],(double)h1[i],(double)h2[i]);
		printf("%lf\n",query(1,l[i],r[i]));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值