题目背景
滚粗了的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位小数)。
输入输出样例
5 5 1 5 4 2 3 2 1 4 3 1 5 1 1 1 1 1 2 2 -1 3 1 5
3.0000 2.0000 0.8000
说明/提示
样例说明:
数据规模:
思路:首先这个题与线段树板子题唯一不同的一点就是要求支持查询方差(平均数可以直接通过和除以区间长度得到),居然从黄题变成了蓝题(不可思议)。那么我们只需要看看多出一个查询方差需要维护什么东西就可以了。首先通过方差公式推导一波:将括号和求和公式拆开,能得到(设t为平均数):
s^2 = [ ( a1 - t ) ^ 2 + ( a2 - t ) ^ 2 + ...... + ( an - t ) ^ 2 ] / n = [ a1 ^ 2 + a2 ^ 2 +......+ an ^ 2 - 2 * t * ( a1 + a2 + ...... + an) + n * t ^ 2 ] / n
线段树的模板支持我们得到序列和,平均数和区间长度,那么我们这个时候只需要来维护一下序列的平方和即可。
那么又一个关键的问题来了,如何push_up?
原先的线段树模板我们让整段序列加上k,和实际上就是加了( r - l + 1 ) * k,那么现在依然是每个数加k,平方和怎么变呢?假设区间内的数为 al,al+1,al+2......ar,将每个数加上k,平方和就变成了( al + k ) ^ 2 + (al+1 + k)^ 2 +......+( ar + k ) ^ 2
同样的把它拆开,我们可以得到:
原式 = al ^ 2 + al+1 ^ 2 +......+ ar ^ 2 +2 * k * ( al +al+1 +......+ ar ) + ( r - l + 1 ) * k ^ 2
发现前面的原序列平方和我们是维护了的,k我们知道,序列和我们知道,区间长度也知道,这样就可以push_up合并了,这个题就解决了。
另外:注意开double!!!
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int maxn=100005; double a[maxn]; int n,q; struct Node{ double v1,v2,tag; int l,r; Node *ls,*rs; Node(const int L,const int R){ l=L,r=R; if(l==r){ tag=0; v1=a[l]; v2=a[l]*a[l];//v2表示序列平方和,初始化为平方 rs=ls=NULL; } else{ tag=0; int M=(L+R)>>1; ls=new Node(L,M); rs=new Node(M+1,R); pushup(); } } inline void pushup(){ v1=ls->v1+rs->v1; v2=ls->v2+rs->v2; } inline void pushdown(){ if(tag==0) return; else{ ls->maketag(tag); rs->maketag(tag); tag=0; } } inline void maketag(double w){ v2=v2+(r-l+1)*w*w+2*w*v1;//根据公式计算,注意一定要先更新v2再更新v1,因为更新v2要用到原序列的和 v1+=(r-l+1)*w; tag+=w; } inline bool InRange(const int L,const int R){ return (l>=L)&&(r<=R); } inline bool OutofRange(const int L,const int R){ return (l>R)||(r<L); } inline void upd(const int L,const int R,double w){ if(InRange(L,R)){ maketag(w); }else if(!OutofRange(L,R)){ pushdown(); ls->upd(L,R,w); rs->upd(L,R,w); pushup(); } } double qry(const int L,const int R){ if(InRange(L,R)){ return v1; } if(OutofRange(L,R)){ return 0; } else{ pushdown(); return ls->qry(L,R)+rs->qry(L,R); } } double qry1(const int L,const int R){ if(InRange(L,R)){ return v2; } if(OutofRange(L,R)){ return 0; } else{ pushdown(); return ls->qry1(L,R)+rs->qry1(L,R); } } }; int main() { scanf("%d%d",&n,&q); for(int i=1;i<=n;i++){ scanf("%lf",a+i); } Node *rot=new Node(1,n); for(int i=1;i<=q;i++){ int o,x,y; double z; scanf("%lld%d%d",&o,&x,&y); if(x>y){ swap(x,y); } if(o==1){ scanf("%lf",&z); rot->upd(x,y,z); } if(o==2){ printf("%.4lf ",(rot->qry(x,y))/(y-x+1)); } if(o==3){ double s1=rot->qry1(x,y);//平方和 double s2=(rot->qry(x,y))/(y-x+1);//平均数 double s3=rot->qry(x,y);//和 printf("%.4lf ",((s1-2*s2*s3+(y-x+1)*s2*s2)/(y-x+1)));//根据公式计算 } } return 0; }