你最近安装了一个新的屏幕保护程序,如果你离开键盘5 分钟,屏保将会显
示一个有热带鱼的水族馆,水族馆的底端是由沙石形成的供鱼玩耍的地方,沙石
的高度可以设置,水位也可以设置。
水族馆可以看做是一个二维平面,宽看作N-1 列,最左端的横坐标为0,最
右端横坐标为N-1,每个整数横坐标都对应着一个沙石的高度H_i(0<=i<=N-1),
相邻横坐标i 和i+1 之间的沙石可以看做是由(i,H_i)和(i+1,H_i+1)这两个点形成
的线段。
如果水位为h,水覆盖着水族馆底端到y=h 这个区域,如果有部分沙石在水
面以上,这部分形成一个岛屿。
对于不同的沙石情况,你想知道被水覆盖区域的面积,即水位以下总面积减
去水中沙石的面积。
开始以为这是一道几何题,然而只是一道代数几何题……
相邻的h之间都存在的一个梯形(或者没有),我们需要求当水漫到水位为x时,覆盖的面积。
怎么求?
初中代数几何题做多了,看到这种题就想到了分类讨论。
假设x=h[i-1],y=h[i],如果x>y就调换。(因为要求的面积是正数)
1、如果当前的水位p≤x,那么覆盖的面积为0。
2、如果当前的水位x≤p≤y,那么覆盖的面积为(用一下三角形相似) p2−2∗p∗x+x22∗(y−x) 。
3、如果当前的水位y≤p,那么覆盖的面积为 p−y−x2+y 。
发现最多只是一元二次方程,而且只用求和运算,修改也只是单点修改,那么放到树状数组上面最方便了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200007;
int i,j,k,l,t,n,m,ans,h[maxn];
double ta[maxn],tb[maxn],tc[maxn],z;
char s[10];
int lowbit(int x){return x&(-x);}
void add(double *x,int y,double z){
while(y<=1100){
x[y]+=z;
y+=lowbit(y);
}
}
double get(double *x,int y){
double u=0;
while(y>0){
u+=x[y];
y-=lowbit(y);
}
return u;
}
double chu(double x,double y){if(y==0)return 0;else return x/y;}
void change(int x,int y,int z){
if(x>y)swap(x,y);
add(ta,x+1,z*chu(1,(2*y-2*x)));
add(tb,x+1,z*chu(-x,(y-x)));
add(tc,x+1,z*chu(x*x,(2*y-2*x)));
add(ta,y+1,(-1)*z*chu(1,(2*y-2*x)));
add(tb,y+1,(-1)*z*chu(-x,(y-x)));
add(tc,y+1,(-1)*z*chu(x*x,(2*y-2*x)));
add(tb,y+1,1*z);
add(tc,y+1,z*(chu((y-x),2)-y));
}
int main(){
scanf("%d%d",&n,&m);
fo(i,1,n)scanf("%d",&h[i]);
fo(i,2,n){
change(h[i-1],h[i],1);
}
fo(t,1,m){
scanf("%s%d",s,&k);
if(s[0]=='Q'){
z=get(ta,k)*k*k+get(tb,k)*k+get(tc,k);
printf("%.3f\n",z);
}
else{
scanf("%d",&l);k++;
if(k!=1)change(h[k],h[k-1],-1);
if(k!=n)change(h[k],h[k+1],-1);
h[k]=l;
if(k!=1)change(h[k],h[k-1],1);
if(k!=n)change(h[k],h[k+1],1);
}
}
}