Description
平面直角坐标系内有n个点,第i个点的坐标为(i,Hi),顺次连接这n个点。
现在给出一条直线y=h,求这条直线以下的由这条直线和其他线段围成的图形的面积。
兹瓷单点修改。
n<=10^5,hi<=1000
语文不好,放图来讲讲道理。
左边这张图的答案是3.75,右图为6.
Solution
考虑hi和hi+1所连的线段。
若
hi<h<hi+1
,那么答案是
(h−hi)22(hi+1−hi)
这是个二次多项式。那么我们可以用树状数组维护当h=x时的每一项的系数。
若
hi+1<=h
,那么答案是
h−hi+1−hi2
。只有一次项和常数项,同样可以做。
具体用树状数组的实现,因为当
h∈[hi,hi+1]
的时候,它所形成的答案的多项式的系数都是一样的,那么就用树状数组的“区间加法”就好了。
剩余的同理。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 100005
#define H 1005
using namespace std;
typedef double db;
db t[3][H];
int n,m,x,y,h[N];
char ch[1];
void add(int z,int x,db y) {
for(x++;x<=H-4;x+=x&-x) t[z][x]+=y;
}
db find(int z,int x) {
db ans=0;
for(x++;x;x-=x&-x) ans+=t[z][x];
return ans;
}
void calc(int x,int y,int f) {
if (x>y) swap(x,y);
if (x!=y) {
db p=1.0*f/(2*(y-x));
add(0,x,p);add(0,y+1,-p);
p=-2.0*x*f/(2*(y-x));
add(1,x,p);add(1,y+1,-p);
p=x*x*f*1.0/(2*(y-x));
add(2,x,p);add(2,y+1,-p);
}
add(1,y+1,f);add(2,y+1,-f*(x+y)*1.0/2);
}
int main() {
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&h[i]);
fo(i,1,n-1) calc(h[i],h[i+1],1);
for(;m;m--) {
scanf("%s%d",ch,&x);
if (ch[0]=='Q') {
db a=find(0,x),b=find(1,x),c=find(2,x);
printf("%.3lf\n",a*x*x+b*x+c);
} else {
scanf("%d",&y);x++;
if (x>1) calc(h[x-1],h[x],-1),calc(h[x-1],y,1);
if (x<n) calc(h[x],h[x+1],-1),calc(y,h[x+1],1);
h[x]=y;
}
}
}