AKVARIJ(ZJNU 1806)
题目大意
你有一个宽度为\(n-1\)的电脑屏幕,左端点的坐标为\(0\),右端点为\(n-1\),你的屏保是一个水族馆,对于每个点都可以自定义一个高度,代表沙丘的高度,水族馆的水的高度也能自定义,范围在\([0,1000]\)内。现在你有每一个点的沙丘的初始高度,每次你有两种操作:\(1、Q\ h\)表示询问当水的高度为\(h\)时水的面积为多少;\(2、U\ 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\),而三角形的沙丘,可以看成减去\(a\cdot i^2+b\cdot i+c\),于是我们就可以直接维护这三个系数,就可以避免写繁琐的线段树维护等差数列,代码就再说叭~

浙公网安备 33010602011771号