正题
题目大意
4个操作
单点修改,区间修改,区间求和,区间求方差
方差为:∑(xi−ave)2n\frac{\sum(x_i-ave)^2}{n}n∑(xi−ave)2
aveaveave为平均值
解题思路
我们将方差的式子分解一下
∑(xi−ave)2n\frac{\sum(x_i-ave)^2}{n}n∑(xi−ave)2
∑(xi2−2∗x∗ave+ave2)n\frac{\sum(x_i^2-2*x*ave+ave^2)}{n}n∑(xi2−2∗x∗ave+ave2)
(∑xi2)−2∗(∑xi)∗ave+n∗ave2n\frac{(\sum x_i^2)-2*(\sum x_i)*ave+n*ave^2}{n}n(∑xi2)−2∗(∑xi)∗ave+n∗ave2
然后因为aveaveave可以用区间和求,所以我们只需要多维护一个区间平方和
之后我们考虑如何快速计算,对于一个区间所有数都加上valvalval,之后的平方和
∑(xi+val)2\sum (x_i+val)^2∑(xi+val)2
我们将其分解
∑(xi2+2∗xi∗val+val2)\sum (x_i^2+2*x_i*val+val^2)∑(xi2+2∗xi∗val+val2)
(∑xi2)+(∑xi)∗2∗val+n∗val2(\sum x_i^2)+(\sum x_i)*2*val+n*val^2(∑xi2)+(∑xi)∗2∗val+n∗val2
然后(∑xi2)(\sum x_i^2)(∑xi2)和(∑xi)(\sum x_i)(∑xi)我们都知道,所以可以直接计算之后的平方和。
codecodecode
#include<cstdio>
#define N 100010
#define ll long long
#define lodu long double
using namespace std;
struct tree_node{ll l,r,w,ww,lazy;
}t[N<<2];
ll n,q;
lodu sum_f;
void build(ll k,ll l,ll r)//建树
{t[k].l=l;t[k].r=r;if(l==r){scanf("%lld",&t[k].w);t[k].ww=t[k].w*t[k].w;return;}ll mid=(l+r)>>1;build(k*2,l,mid);build(k*2+1,mid+1,r);t[k].w=t[k*2].w+t[k*2+1].w;t[k].ww=t[k*2].ww+t[k*2+1].ww;
}
void get_fan(ll k,ll z)//求区间加了z之后的区间平方和
{t[k].ww=t[k].ww+t[k].w*2*z+z*z*(t[k].r-t[k].l+1);}
void downdata(ll k){if(t[k].lazy==0) return;get_fan(k*2,t[k].lazy);get_fan(k*2+1,t[k].lazy); t[k*2].w+=t[k].lazy*(t[k*2].r-t[k*2].l+1);t[k*2+1].w+=t[k].lazy*(t[k*2+1].r-t[k*2+1].l+1);t[k*2].lazy+=t[k].lazy;t[k*2+1].lazy+=t[k].lazy;t[k].lazy=0;
}//下传lazy标记
void change(ll k,ll l,ll r,ll z)//区间修改
{if(t[k].l==l&&t[k].r==r){get_fan(k,z);t[k].lazy+=z;t[k].w+=z*(r-l+1);return;}downdata(k);if(r<=t[k*2].r) change(k*2,l,r,z);else if(l>=t[k*2+1].l) change(k*2+1,l,r,z);else change(k*2,l,t[k*2].r,z),change(k*2+1,t[k*2+1].l,r,z);t[k].ww=t[k*2].ww+t[k*2+1].ww;t[k].w=t[k*2].w+t[k*2+1].w;
}
ll get_sum(ll k,ll l,ll r)//区间求和和平方和
{if(t[k].l==l&&t[k].r==r){sum_f+=t[k].ww;return t[k].w;}downdata(k);if(r<=t[k*2].r) return get_sum(k*2,l,r);if(l>=t[k*2+1].l) return get_sum(k*2+1,l,r);return get_sum(k*2,l,t[k*2].r)+get_sum(k*2+1,t[k*2+1].l,r);
}
int main()
{//freopen("data.in","r",stdin);//freopen("data.out","w",stdout);scanf("%lld%lld",&n,&q);build(1,1,n);for(ll i=1;i<=q;i++){ll ts,a,b,z;scanf("%lld%lld%lld",&ts,&a,&b);if(ts==0) change(1,a,a,b);else if(ts==1){scanf("%lld",&z);change(1,a,b,z);}else if(ts==2) sum_f=0,printf("%lld\n",get_sum(1,a,b));else{sum_f=0;lodu ave,ans,sum=get_sum(1,a,b),n=b-a+1;ave=sum/n;ans=sum_f+ave*ave*n-sum*ave*2; printf("%0.10Lf\n",ans/n);}}
}