P1471 方差
题目描述
蒟蒻HansBug在一本数学书里面发现了一个神奇的数列,包含N个实数。他想算算这个数列的平均数和方差。
输入格式
第一行包含两个正整数N、M,分别表示数列中实数的个数和操作的个数。
第二行包含N个实数,其中第i个实数表示数列的第i项。
接下来M行,每行为一条操作,格式为以下两种之一:
操作1:1 x y k ,表示将第x到第y项每项加上k,k为一实数。
操作2:2 x y ,表示求出第x到第y项这一子数列的平均数。
操作3:3 x y ,表示求出第x到第y项这一子数列的方差。
输出格式
输出包含若干行,每行为一个实数,即依次为每一次操作2或操作3所得的结果(所有结果四舍五入保留4位小数)。
输入 #1
5 5
1 5 4 2 3
2 1 4
3 1 5
1 1 1 1
1 2 2 -1
3 1 5
输出 #1
3.0000
2.0000
0.8000
说明/提示
这道题很明显是一道线段树了,平均数好求,只要维护区间和就行;难在方差,方差和平均数的关系我不知道,这道题考的就是这个,足以体现数学的重要性;这里贴两张图片就非常明了了,我们只要维护区间和,区间平方和就行了;
把方差展开:
然后维护一个平方和,那么平方和怎么维护呢?
代码:
#include<bits/stdc++.h>
using namespace std;
double a[100100];
int q,x,y;
double z;
double sum;//和
struct Node{
int l,r;
double w,f,s;//w为和,s为平方和
}tree[400100];
inline void pp(int k){
tree[k].w=(tree[k<<1].w+tree[k<<1|1].w);
tree[k].s=tree[k<<1].s+tree[k<<1|1].s;
}
inline void build(int k,int ll,int rr){
tree[k].l=ll,tree[k].r=rr,tree[k].f=0.0;
if(ll==rr){
tree[k].w=a[ll];
tree[k].s=a[ll]*a[ll];
return;
}
int m=(ll+rr)>>1;
build(k<<1,ll,m);
build(k<<1|1,m+1,rr);
pp(k);
}
inline void pd(int k){
if(tree[k].f){
tree[k<<1].f+=tree[k].f;
tree[k<<1|1].f+=tree[k].f;
tree[k<<1].s+=(double)(2*tree[k].f*(tree[k<<1].w)+(tree[k<<1].r-tree[k<<1].l+1)*tree[k].f*tree[k].f);
tree[k<<1|1].s+=(double)(2*tree[k].f*(tree[k<<1|1].w)+(tree[k<<1|1].r-tree[k<<1|1].l+1)*tree[k].f*tree[k].f);
tree[k<<1].w+=(double)tree[k].f*(tree[k<<1].r-tree[k<<1].l+1);
tree[k<<1|1].w+=(double)tree[k].f*(tree[k<<1|1].r-tree[k<<1|1].l+1);
tree[k].f=0;
return;
}
}
inline void change(int k){
if(tree[k].l>=x&&tree[k].r<=y){
tree[k].s+=(double)(2*z*(tree[k].w)+(tree[k].r-tree[k].l+1)*z*z);
tree[k].w+=(double)(tree[k].r-tree[k].l+1)*z;
tree[k].f+=z;
return;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) change(k<<1);
if(y>m) change(k<<1|1);
pp(k);
}
inline void ask1(int k){//区间查询
if(tree[k].l>=x&&tree[k].r<=y){
sum+=tree[k].w;
return;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) ask1(k<<1);
if(y>m) ask1(k<<1|1);
}
inline void ask2(int k){
if(tree[k].l>=x&&tree[k].r<=y){
sum+=tree[k].s;
return;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) ask2(k<<1);
if(y>m) ask2(k<<1|1);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lf",&a[i]);
build(1,1,n);
while(m--){
scanf("%d",&q);
if(q==1){
scanf("%d%d%lf",&x,&y,&z);
change(1);
}
else if(q==2){
scanf("%d%d",&x,&y);
sum=0;//和
ask1(1);
printf("%.4lf\n",(double)1.0*sum/(y-x+1));
}
else{
sum=0;//和
scanf("%d%d",&x,&y);
ask1(1);
double S=(double)1.0*sum/(y-x+1);//平均数
sum=0;//平方和
ask2(1);
printf("%.4lf\n",(double)(sum/(y-x+1)-S*S));
}
}
return 0;
}