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;
}
posted @ 2023-06-23 22:19  alex_liu09  阅读(29)  评论(0)    收藏  举报