AKVARIJ(ZJNU 1806)

题目大意

你有一个宽度为n1的电脑屏幕,左端点的坐标为0,右端点为n1,你的屏保是一个水族馆,对于每个点都可以自定义一个高度,代表沙丘的高度,水族馆的水的高度也能自定义,范围在[0,1000]内。现在你有每一个点的沙丘的初始高度,每次你有两种操作:1Q h表示询问当水的高度为h时水的面积为多少;2U i h表示把坐标为i的点的沙丘的高度调整为h。对于每一次操作一,输出要求的答案。

思路一

由于是对高度经行询问,我们考虑把每一层水的面积当成叶子节点建一棵线段树,这样每一次修改,我们都可以把他转化为对区间加上一个等差数列,这样问题就很好的得到了解决。

代码一

#include<bits/stdc++.h>
using namespace std;
int n,m;
double tree[1005<<2],laz1[1005<<2],laz2[1005<<2];
void pushdown(int p,int l,int r)
{
	int mid=(l+r)>>1;
	double x=laz1[p],y=laz2[p];
	laz1[p<<1]+=x;
	laz2[p<<1]+=y;
	laz1[p<<1|1]+=x+y*(mid-l+1);
	laz2[p<<1|1]+=y;
	tree[p<<1]+=(x+x+(mid-l)*y)*(mid-l+1)/2;
	tree[p<<1|1]+=(x+y*(mid-l+1)+x+y*(mid-l+1)+(r-mid-1)*y)*(r-mid)/2;
	laz1[p]=laz2[p]=0;
}
void build(int p,int l,int r)
{
	laz1[p]=laz2[p]=0;
	if(l==r)
	{tree[p]=n-1;
	else
	{
		int mid=(l+r)>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		tree[p]=tree[p<<1]+tree[p<<1|1];
	}
}
void update(int p,int l,int r,double v,double d,int x,int y)
{
	if(x<=l&&r<=y)
	{
		double vv=v+(l-x)*d;
		laz1[p]+=vv;
		laz2[p]+=d;
		tree[p]+=(vv+vv+d*(r-l))*(r-l+1)/2;
	}
	else
	{
		int mid=(l+r)>>1;
		pushdown(p,l,r);
		if(x<=mid)update(p<<1,l,mid,v,d,x,y);
		if(mid<y)update(p<<1|1,mid+1,r,v,d,x,y);
		tree[p]=tree[p<<1]+tree[p<<1|1];
	}
}
double query(int p,int l,int r,int x,int y)
{
	if(x<=l&&r<=y)return tree[p];
	else
	{
		pushdown(p,l,r);
		int mid=(l+r)>>1;
		double ans=0;
		if(x<=mid)ans+=query(p<<1,l,mid,x,y);
		if(mid<y)ans+=query(p<<1|1,mid+1,r,x,y);
		tree[p]=tree[p<<1]+tree[p<<1|1];
		return ans;
	}
}
int h[100005];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)scanf("%d",&h[i]);
	build(1,1,1000);
	for(int i=0;i<n-1;i++)
	{
		int minn=min(h[i],h[i+1]);
		int maxx=max(h[i],h[i+1]);
		if(minn>=1)update(1,1,1000,-1,0,1,minn);
		if(maxx>minn)update(1,1,1000,-(1.0-1.0/(maxx-minn)/2),1.0/(maxx-minn),minn+1,maxx);
	}
	while(m--)
	{
		char op[10];
		scanf("%s",op+1);
		if(op[1]=='Q')
		{
			int H;
			scanf("%d",&H);
			if(H==0)printf("0.000\n");
			else printf("%.3f\n",query(1,1,1000,1,H));
		}
		else
		{
			int id,H;
			scanf("%d%d",&id,&H);
			if(id>0)
			{
				if(h[id]>h[id-1])
				{
					update(1,1,1000,1.0-1.0/(h[id]-h[id-1])/2,-1.0/(h[id]-h[id-1]),h[id-1]+1,h[id]);
				}
				else if(h[id]<h[id-1])
				{
					update(1,1,1000,-1.0/(h[id-1]-h[id])/2,-1.0/(h[id-1]-h[id]),h[id]+1,h[id-1]);
				}
				if(H>h[id-1])
				{
					update(1,1,1000,-(1.0-1.0/(H-h[id-1])/2),1.0/(H-h[id-1]),h[id-1]+1,H);
				}
				else if(H<h[id-1])
				{
					update(1,1,1000,1.0/(h[id-1]-H)/2,1.0/(h[id-1]-H),H+1,h[id-1]);
				}
			}
			if(id<n-1)
			{
				if(h[id]>h[id+1])
				{
					update(1,1,1000,1.0-1.0/(h[id]-h[id+1])/2,-1.0/(h[id]-h[id+1]),h[id+1]+1,h[id]);
				}
				else if(h[id]<h[id+1])
				{
					update(1,1,1000,-1.0/(h[id+1]-h[id])/2,-1.0/(h[id+1]-h[id]),h[id]+1,h[id+1]);
				}
				if(H>h[id+1])
				{
					update(1,1,1000,-(1.0-1.0/(H-h[id+1])/2),1.0/(H-h[id+1]),h[id+1]+1,H);
				}
				else if(H<h[id+1])
				{
					update(1,1,1000,1.0/(h[id+1]-H)/2,1.0/(h[id+1]-H),H+1,h[id+1]);
				}
			}
			h[id]=H;
		}
	}
	return 0;
}

思路二

我们还能发现,如果把深度为i的水的面积为叶子节点建一棵线段树,那么对于一块方形的沙丘,相当于减去i,而三角形的沙丘,可以看成减去ai2+bi+c,于是我们就可以直接维护这三个系数,就可以避免写繁琐的线段树维护等差数列,代码就再说叭~


__EOF__

本文作者Jerry-Black
本文链接https://www.cnblogs.com/Jerry-Black/p/16455950.html
关于博主:小蒟蒻一只( ̄^ ̄)ゞ
版权声明:转载请注明来源哟~ QAQ
声援博主:UP UP UP !!!
posted @   Jerry_Black  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示