hdu 3340 Rain in ACStar(成段更新)

题意:有T组测试数据,每组数据有N个操作,(1)“R P”,表示掉下了一个P边形,接下来的一行里,给出这P个点的坐标——首先是最左边的点,然后从这点开始逆时针给出其他点。(2)"Q A B“,查询数轴上区间[A,B]内面积的和。


        易知,图1里的那个三角形的面积是通过,图2里红色的面积加上图3里绿色的面积,然后减去图4里黄色的面积。

        这些图形都可以转化成梯形,即在一段区间[A,B]里,A加上首项为add1,B加上末项为add2,长度为B-A+1,公差是(add2-add1)/(B-A+1)的等差数列。

        我们从最左边的那一点开始遍历,如果当前点的x坐标小于它的下一点的x坐标,那么就减去由当前点、下一点以及数轴构成的梯形的面积,反之则加上。那么问题就是延迟标记是什么,如何传递。这就和 Uva 12436 Rip Van Winkle's Code这道题很类似(解题报告Here)。等差数列叠加了,还是等差数列,性质没有改变。所以在线段树的结点里,记录左端点要加多少add1,右端点要加多少add2,以及公差step。向下传递的时候,将区间分解成两部分,区间中点要加多少,可以通过等差数列的分工计算出来。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
#define MID(a,b) (a+((b-a)>>1))
const int N=125010;

struct node
{
	int lft,rht,len;
	double add1,add2,step,sum;
	int mid(){return MID(lft,rht);}
	void init() { add1=add2=step=sum=0; }
	void fun(double a,double b,double c)
	{
		add1+=a;	add2+=b; 	step+=c;
		sum+=(a+b)*len/2;
	}
};

vector<int> sca;
map<int,int> H;

struct OP
{
	int type,n;
	int x[6],y[6];
	void get()
	{
		char str[5];
		scanf("%s",str);
		if(str[0]=='R')
		{
			type=0;
			scanf("%d",&n);
			for(int i=0;i<n;i++)
			{
				scanf("%d%d",&x[i],&y[i]);
				sca.push_back(x[i]);
			}
		}
		else
		{
			type=1;
			scanf("%d%d",&x[0],&y[0]);
			sca.push_back(x[0]);
			sca.push_back(y[0]);
		}
	}
}op[N/5];
struct Segtree
{
	node tree[N*4];
	double calu(int st,int ed,double add1,double step)
	{
		int len=sca[ed]-sca[st];
		return add1+len*step;
	}
	void PushUp(int ind)
	{
		tree[ind].sum=tree[LL(ind)].sum+tree[RR(ind)].sum;
	}
	void PushDown(int ind)
	{

			double add1=tree[ind].add1,add2=tree[ind].add2,step=tree[ind].step;
			double tmp=calu(tree[ind].lft,tree[ind].mid(),add1,step);
			tree[LL(ind)].fun(add1,tmp,step);
			tree[RR(ind)].fun(tmp,add2,step);
			tree[ind].add1=tree[ind].add2=tree[ind].step=0;
	}
	void build(int lft,int rht,int ind)
	{
		tree[ind].lft=lft;	tree[ind].rht=rht;
		tree[ind].init();	tree[ind].len=sca[rht]-sca[lft];
		if(lft+1!=rht)
		{
			int mid=tree[ind].mid();
			build(lft,mid,LL(ind));
			build(mid,rht,RR(ind));
		}
	}
	void updata(int st,int ed,int ind,double add1,double add2,double step)
	{
		int lft=tree[ind].lft,rht=tree[ind].rht;
		if(st<=lft&&rht<=ed) tree[ind].fun(add1,add2,step);
		else
		{
			PushDown(ind);
			int mid=tree[ind].mid();
			if(ed<=mid) updata(st,ed,LL(ind),add1,add2,step);
			else if(st>=mid) updata(st,ed,RR(ind),add1,add2,step);
			else
			{
				double tmp=calu(st,mid,add1,step);
				updata(st,mid,LL(ind),add1,tmp,step);
				updata(mid,ed,RR(ind),tmp,add2,step);
			}
			PushUp(ind);
		}
	}
	double query(int st,int ed,int ind)
	{
		int lft=tree[ind].lft,rht=tree[ind].rht;
		if(st<=lft&&rht<=ed) return tree[ind].sum;
		else
		{
			PushDown(ind);
			int mid=tree[ind].mid();
			double sum=0;
			if(st<mid) sum+=query(st,ed,LL(ind));
			if(ed>mid) sum+=query(st,ed,RR(ind));
			PushUp(ind);
			return sum;
		}
	}
}seg;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		sca.clear(); H.clear();

		int n;
		scanf("%d",&n);
		for(int i=0;i<n;i++) op[i].get();

		sort(sca.begin(),sca.end());
		sca.erase(unique(sca.begin(),sca.end()),sca.end());
		for(int i=0;i<(int)sca.size();i++) H[sca[i]]=i;

		seg.build(0,(int)sca.size()-1,1);
		for(int i=0;i<n;i++)
		{
			int x1,y1,x2,y2;
			if(op[i].type==0)
			{
				for(int j=0;j<op[i].n;j++)
				{
					x1=op[i].x[j];	y1=op[i].y[j];
					x2=op[i].x[(j+1)%op[i].n];	y2=op[i].y[(j+1)%op[i].n];
					if(x1>x2) swap(x1,x2),swap(y1,y2);
					else y1=-y1,y2=-y2;
					double step=(y1*1.-y2*1.)/(x1*1.-x2*1.);
					seg.updata(H[x1],H[x2],1,y1,y2,step);
				}
			}
			else
			{
				x1=op[i].x[0],y1=op[i].y[0];
				printf("%.3lf\n",seg.query(H[x1],H[y1],1));
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值