您的位置:首页 > 其它

洛谷 P1471 方差

2017-09-03 17:17 246 查看
题目背景

滚粗了的HansBug在收拾旧数学书,然而他发现了什么奇妙的东西。

题目描述

蒟蒻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

说明



样例说明:



数据规模:



//对于维护平均值这个东西,线段树是小菜一碟,随便你怎么搞
//然后对于方差;她有个公式的:
//S=[(a-Z)2+(b-Z)2+(c-Z)2+(d-Z)2+(e-Z)2...]/n(Z为平均数)
//S=[区间平方和-2*n*平均数^2+n*平均数^2]/n;
//S=[区间平方和-n*平均数2]/n;
//所以维护个平方和就好了;
//然后,平方和怎么区间加呢?
//她也有个公式:
//(a+p)^2+(b+p)^2+(c+p)^2+(d+p)^2+(e+p)^2
//=以前平方和+(2*p*(Z*n)+n*p*p);
//综上所述,对于方差的维护只维护一个区间平方和就可以了
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 100050
#define INF 0
struct Segment_Tree{
int l,r;
double sum,sum_squ,p,lazy;
}tre[MAXN<<2];
inline void UpDate(int u){
tre[u].sum=tre[u<<1].sum+tre[u<<1|1].sum;
tre[u].sum_squ=tre[u<<1].sum_squ+tre[u<<1|1].sum_squ;
tre[u].p=tre[u].sum/(tre[u].r-tre[u].l+1);
}
#define lc u<<1
#define rc u<<1|1
inline void PushDown(int u){
tre[lc].lazy+=tre[u].lazy,tre[rc].lazy+=tre[u].lazy;

int n=tre[lc].r-tre[lc].l+1; double z=tre[u].lazy;
tre[lc].sum_squ+=n*(z*2*tre[lc].p+z*z);
tre[lc].p+=z,tre[lc].sum+=n*z;

n=tre[rc].r-tre[rc].l+1; z=tre[u].lazy;
tre[rc].sum_squ+=n*(z*2*tre[rc].p+z*z);
tre[rc].p+=z,tre[rc].sum+=z*n;
tre[u].lazy=INF;
}

void Build(int u,int l,int r){
tre[u].l=l,tre[u].r=r,tre[u].lazy=INF;
if(l==r){
scanf("%lf",&tre[u].sum);
tre[u].sum_squ=tre[u].sum*tre[u].sum;
tre[u].p=tre[u].sum;
return ;
}
int Mid=l+r>>1;
Build(u<<1,l,Mid),Build(u<<1|1,Mid+1,r);
UpDate(u);
}

void Modify(int u,int l,int r,double k){
if(l<=tre[u].l&&tre[u].r<=r){
tre[u].lazy+=k;
tre[u].sum+=(tre[u].r-tre[u].l+1)*k;
tre[u].sum_squ+=(tre[u].r-tre[u].l+1)*(k*2*tre[u].p+k*k);
tre[u].p+=k;
return ;
}
if(tre[u].lazy!=INF) PushDown(u);
int Mid=(tre[u].l+tre[u].r)>>1;
if(r<=Mid) Modify(u<<1,l,r,k);
else if(l>Mid) Modify(u<<1|1,l,r,k);
else Modify(u<<1,l,Mid,k),Modify(u<<1|1,Mid+1,r,k);
UpDate(u);
}

double query(int u,int l,int r,int opt){
if(l<=tre[u].l&&tre[u].r<=r){
if(opt==2) return tre[u].sum;
if(opt==3) return tre[u].sum_squ;
}
if(tre[u].lazy!=INF) PushDown(u);
int Mid=tre[u].l+tre[u].r>>1;
if(l>Mid) return query(u<<1|1,l,r,opt);
else if(r<=Mid) return query(u<<1,l,r,opt);
else return query(u<<1,l,Mid,opt)+query(u<<1|1,Mid+1,r,opt);
}

int main(){
int n,m;
scanf("%d%d",&n,&m);
Build(1,1,n);

for(int l,r,opt,i=1;i<=m;++i){
double k;
scanf("%d%d%d",&opt,&l,&r);
int kk=(r-l+1);
if(opt==1) scanf("%lf",&k),Modify(1,l,r,k);
if(opt==2)
printf("%.4lf\n",query(1,l,r,opt)/(r-l+1));
if(opt==3){
double sum_square=query(1,l,r,opt);//该区间的平方和
double Average=query(1,l,r,2);//该区间的平均值
Average/=kk;
printf("%.4lf\n",(sum_square-kk*Average*Average)/kk);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树 数学