- 题目描述
蒟蒻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位小数)。
竟然用到了数学课本上的知识
真心好题
首先,(算术)平均数
a¯=∑ni=1ain
方差的两个最基本的形式
s2=∑ni=1(a¯−ai)2n=∑ni=1a2in−(∑ni=1ai)2n2
这道题这么明显的区间操作,显然容易想到线段树。
操作1和2都好说,但是维护方差真的好屎啊(而且还不会)。
只需要在原来的区间和以及lazy tag的基础上再多维护一个区间平方和( ∑ni=1a2i )就行了。
操作2就不说了,显而易见怎么做。
操作3也很简单,先求出区间和以及平方和,按式子搞就行了。
操作1就考虑怎么下放tag。
区间和就不说了。
假设区间 [l,r] 增加 k 。那么其实就是变成
==∑i=lr(ai+k)2∑i=lr(a2i+2×k×ai+k2)∑i=lra2i+2k×∑i=lrai+(r−l+1)×k2
然后随便搞搞就行了。
#include <bits/stdc++.h>
using namespace std ;
const int maxn = 1e5+5, maxm = maxn<<2 ;
bool Read ( int &x, char c = getchar(), bool flag = false ) {
for ( x = 0 ; !isdigit(c) ; c = getchar() ) if ( c == EOF ) return false ; else if ( c == '-' ) flag = true ;
for ( ; isdigit(c) ; c = getchar() ) x = 10*x + c - '0' ; if ( flag ) x = -x ;
return true ;
}
int n, m ;
double tree[maxm][2], tag[maxm] ;
// tree[0] = \sum a_i tree[1] = \sum{a_i^2}
void push_up ( int h ) {
tree[h][0] = tree[h<<1][0]+tree[h<<1|1][0] ;
tree[h][1] = tree[h<<1][1]+tree[h<<1|1][1] ;
}
void push_down ( int h, int len ) {
if ( fabs(tag[h])>1e-9 ) {
tag[h<<1] += tag[h] ;
tag[h<<1|1] += tag[h] ;
int len1 = len-(len>>1), len2 = len>>1 ;
tree[h<<1][1] += 2*tag[h]*tree[h<<1][0]+len1*tag[h]*tag[h] ;
tree[h<<1|1][1] += 2*tag[h]*tree[h<<1|1][0]+len2*tag[h]*tag[h] ;
tree[h<<1][0] += tag[h]*len1 ;
tree[h<<1|1][0] += tag[h]*len2 ;
tag[h] = 0.0 ;
}
}
void create ( int h, int l, int r ) {
if ( l == r ) {
scanf ( "%lf", &tree[h][0] ) ;
tree[h][1] = tree[h][0]*tree[h][0] ;
return ;
}
int mid = l+r>>1 ;
create ( h<<1, l, mid ) ;
create ( h<<1|1, mid+1, r ) ;
push_up(h) ;
}
void update ( int h, int l, int r, int x, int y, double k ) {
if ( x <= l && r <= y ) {
tag[h] += k ;
tree[h][1] += 2*k*tree[h][0]+(r-l+1)*k*k ;
tree[h][0] += (r-l+1)*k ;
return ;
}
int mid = l+r>>1 ;
push_down(h, r-l+1) ;
if ( y <= mid ) update ( h<<1, l, mid, x, y, k ) ;
else if ( x > mid ) update ( h<<1|1, mid+1, r, x, y, k ) ;
else {
update ( h<<1, l, mid, x, mid, k ) ;
update ( h<<1|1, mid+1, r, mid+1, y, k ) ;
}
push_up(h) ;
}
double query ( int h, int l, int r, int x, int y, bool type ) {
if ( x <= l && r <= y ) return tree[h][type] ;
int mid = l+r>>1 ;
push_down(h, r-l+1) ;
if ( y <= mid ) return query ( h<<1, l, mid, x, y, type ) ;
else if ( x > mid ) return query ( h<<1|1, mid+1, r, x, y, type ) ;
return query ( h<<1, l, mid, x, mid, type ) + query ( h<<1|1, mid+1, r, mid+1, y, type ) ;
}
int main() {
int i, j, c, x, y ;
double k1, k2, k ;
Read(n) ; Read(m) ;
create(1,1,n) ;
while (m--) {
Read(c) ; Read(x) ; Read(y) ;
if ( c==1 ) {
scanf ( "%lf", &k ) ;
update(1,1,n,x,y,k) ;
} else if ( c==2 ) printf ( "%.4lf\n", (double)query(1,1,n,x,y,0)/(y-x+1) ) ;
else {
k1 = query(1,1,n,x,y,1)/(y-x+1) ;
k2 = query(1,1,n,x,y,0) ;
k2 *= k2 ;
k2 /= (double)(y-x+1)*(y-x+1) ;
k = k1-k2 ;
printf ( "%.4lf\n", (double)k ) ;
}
}
return 0 ;
}