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
样例输入 #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
提示
关于方差:对于一个有 \(n\) 项的数列 \(A\),其方差 \(s^2\) 定义如下:
\[s^2=\frac{1}{n}\sum\limits_{i=1}^n\left(A_i-\overline A\right)^2
\]
其中 \(\overline A\) 表示数列 \(A\) 的平均数,\(A_i\) 表示数列 \(A\) 的第 \(i\) 项。
样例说明:
| 操作步骤 | 输入内容 | 操作要求 | 数列 | 输出结果 | 说明 |
|---|---|---|---|---|---|
| \(0\) | - | - | 1 5 4 2 3 |
- | - |
| \(1\) | 2 1 4 |
求 \(\left[1,4\right]\) 内所有数字的平均数 | 1 5 4 2 3 |
3.0000 |
平均数 \(=\left(1+5+4+2\right)\div 4=3.0000\) |
| \(2\) | 3 1 5 |
求 \(\left[1,5\right]\) 内所有数字的方差 | 1 5 4 2 3 |
2.0000 |
平均数 \(=\left(1+5+4+2+3\right)\div 5=3\),方差 \(=\left(\left(1-3\right)^2+\left(5-3\right)^2+\left(4-3\right)^2+\left(2-3\right)^2+\left(3-3\right)^2\right)\div 5=2.0000\) |
| \(3\) | 1 1 1 1 |
将 \(\left[1,1\right]\) 内所有数字加 \(1\) | 2 5 4 2 3 |
- | - |
| \(4\) | 1 2 2 -1 |
将 \(\left[2,2\right]\) 内所有数字加 \(-1\) | 2 4 4 2 3 |
- | - |
| \(5\) | 3 1 5 |
求 \(\left[1,5\right]\) 内所有数字的方差 | 2 4 4 2 3 |
0.8000 |
平均数 \(=\left(2+4+4+2+3\right)\div 5=3\),方差 \(=\left(\left(2-3\right)^2+\left(4-3\right)^2+\left(4-3\right)^2+\left(2-3\right)^2+\left(3-3\right)^2\right)\div 5=0.8000\) |
数据规模:
| 数据点 | \(N\) | \(M\) | 备注 |
|---|---|---|---|
| \(1\sim3\) | \(N\le 8\) | \(M\le 15\) | - |
| \(4\sim7\) | \(N\le 10^5\) | \(M\le 10^5\) | 不包含操作 \(3\) |
| \(8\sim10\) | \(N\le 10^5\) | \(M\le 10^5\) | - |
分析
设 \(a_l,a_{l+1},...,a_r\) 的平均数为 \(\overline{x}\),考虑把方差的柿子展开
\[s^2=\frac{1}{r-l+1}\sum\limits_{i=l}^r\left(a_i-\overline x\right)^2
\]
\[=\frac{(a_l^2+a_{l+1}^2+...+a_r^2+(r-l+1)\times\overline x^2-(2a_l\overline x+2a_{l+1}\overline x+...+2a_r\overline x)}{r-l+1}
\]
维护当前区间和 \(p\) 和区间平方和 \(q\) ,则:
\[s^2=\overline x^2+\frac{q-2p\overline x}{r-l+1}
\]
\[=\overline x^2-2\overline x^2+\frac{q}{r-l+1}
\]
\[=-\overline x^2+\frac{q}{r-l+1}
\]
发现可区间维护,线段树做即可,注意下传标记时的优先级
#include<bits/stdc++.h>
using namespace std;
double t1[100005<<2],t2[100005<<2],tag[100005<<2],a[100005];
int n,m;
void pushup(int num){t1[num]=t1[num<<1]+t1[num<<1|1],t2[num]=t2[num<<1]+t2[num<<1|1];}
void pushdown(int num,int l,int r){
if(tag[num]){
int mid=l+r>>1;double k=tag[num];
t2[num<<1]+=k*k*(mid-l+1)+2*t1[num<<1]*k;
t2[num<<1|1]+=k*k*(r-mid)+2*t1[num<<1|1]*k;
t1[num<<1]+=k*(mid-l+1),t1[num<<1|1]+=k*(r-mid);
tag[num<<1]+=k,tag[num<<1|1]+=k;
tag[num]=0;
}
}
void build(int l,int r,int num){
if(l==r){
t1[num]=a[l],t2[num]=t1[num]*t1[num];
return;
}
int mid=l+r>>1;
build(l,mid,num<<1),build(mid+1,r,num<<1|1);
pushup(num);
}
void update(int l,int r,int num,int x,int y,double k){
if(x>r||y<l)return;
if(x<=l&&r<=y){
tag[num]+=k;
t2[num]+=k*k*(r-l+1)+(k*t1[num]*2);
t1[num]+=k*(r-l+1);
return;
}
int mid=l+r>>1;
pushdown(num,l,r);
update(l,mid,num<<1,x,y,k),update(mid+1,r,num<<1|1,x,y,k);
pushup(num);
}
double query1(int l,int r,int num,int x,int y){
if(x>r||y<l)return 0;
if(x<=l&&r<=y)return t1[num];
pushdown(num,l,r);
int mid=l+r>>1;
return query1(l,mid,num<<1,x,y)+query1(mid+1,r,num<<1|1,x,y);
}
double query2(int l,int r,int num,int x,int y){
if(x>r||y<l)return 0;
if(x<=l&&r<=y)return t2[num];
pushdown(num,l,r);
int mid=l+r>>1;
return query2(l,mid,num<<1,x,y)+query2(mid+1,r,num<<1|1,x,y);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,n,1);
for(int i=1,opt,x,y;i<=m;i++){
cin>>opt>>x>>y;
if(opt==1){
double k;cin>>k;
update(1,n,1,x,y,k);
}else if(opt==2){
printf("%.4f\n",query1(1,n,1,x,y)/(y-x+1));
}else{
double ans=query2(1,n,1,x,y)/(y-x+1)-pow(query1(1,n,1,x,y)/(y-x+1),2);
printf("%.4f\n",ans);
}
}
return 0;
}

浙公网安备 33010602011771号