【题目背景:】
滚粗了的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
【算法分析:】
支持区间修改,区间询问,常规做法线段树
关于平均值;
维护一个区间和,区间平均值就是区间和/区间长度
关于方差:
设ave为序列a的平均值
s2 = [(a1 - ave)2 + (a2 - ave)2 + ... + (an - ave)2] / n
展开可得:s2 = a12 + a22 + ... + an2 - 2 * ave(a1 + a2 + ... + an) + n * ave2
可以发现,只需要多维护一个区间平方和,就可以求出方差。
但如果要修改某一区间内元素的值呢?
(a1 + v)2 + (a2 + v)2 + ... + (an + v)2
= a12 + a22 + ... + an2 + 2v(a1 + a2 + .. + an) + n * v2
维护区间平方和时,标记下传和修改被包含区间值的操作是一样的,答案就很显然了.
注意坑点:读入的序列是实型,方差平均值和要修改的值也是实型,修改时传参的修改值也要是实型.
【代码:】
1 //P1471 方差 2 #include<iostream> 3 #include<cstdio> 4 using namespace std; 5 6 const int MAXN = 1e5 + 1; 7 8 int n, m; 9 double a[MAXN]; 10 struct Segment { 11 double sum, squ, lazy; 12 }t[MAXN << 2]; 13 14 void Build(int o, int l, int r) { 15 if(l == r) { 16 t[o].sum = a[l]; 17 t[o].squ = a[l] * a[l]; 18 } 19 else { 20 int mid = (l + r) >> 1; 21 Build(o << 1, l, mid); 22 Build(o << 1|1, mid + 1, r); 23 t[o].sum = t[o << 1].sum + t[o << 1|1].sum; 24 t[o].squ = t[o << 1].squ + t[o << 1|1].squ; 25 } 26 } 27 inline void down(int o, int len) { 28 if(!t[o].lazy) return; 29 t[o << 1].squ += 2 * t[o].lazy * t[o << 1].sum 30 + t[o].lazy * t[o].lazy * (len - (len >> 1)); 31 t[o << 1|1].squ += 2 * t[o].lazy * t[o << 1|1].sum 32 + t[o].lazy * t[o].lazy * (len >> 1); 33 t[o << 1].sum += t[o].lazy * (len - (len >> 1)); 34 t[o << 1|1].sum += t[o].lazy * (len >> 1); 35 t[o << 1].lazy += t[o].lazy; 36 t[o << 1|1].lazy += t[o].lazy; 37 t[o].lazy = 0; 38 } 39 void Update(int o, int l, int r, int ul, int ur, double v) { 40 if(ul <= l && r <= ur) { 41 t[o].squ += 2 * v * t[o].sum + v * v * (r - l + 1); 42 t[o].sum += v * (r - l + 1); 43 t[o].lazy += v; 44 } 45 else { 46 down(o, r - l + 1); 47 int mid = (l + r) >> 1; 48 if(ul <= mid) Update(o << 1, l, mid, ul, ur, v); 49 if(ur > mid) Update(o << 1|1, mid + 1, r, ul, ur, v); 50 t[o].sum = t[o << 1].sum + t[o << 1|1].sum; 51 t[o].squ = t[o << 1].squ + t[o << 1|1].squ; 52 } 53 } 54 55 double ssum, ssqu; 56 void Query(int o, int l, int r, int ql, int qr) { 57 if(ql <= l && r <= qr) { 58 ssum += t[o].sum; 59 ssqu += t[o].squ; 60 } 61 else { 62 down(o, r - l + 1); 63 int mid = (l + r) >> 1; 64 if(ql <= mid) Query(o << 1, l, mid, ql, qr); 65 if(qr > mid) Query(o << 1|1, mid + 1, r, ql, qr); 66 } 67 } 68 69 inline double Average(int l, int r) { 70 ssum = ssqu = 0; 71 Query(1, 1, n, l, r); 72 return ssum * 1.0 / (r - l + 1); 73 } 74 inline double S2(int l, int r) { 75 ssum = ssqu = 0; 76 int num = r - l + 1; 77 Query(1, 1, n, l, r); 78 double ave = ssum * 1.0 / num; 79 double ret = ssqu - 2 * ave * ssum + num * ave * ave; 80 return ret / num; 81 } 82 83 int main() { 84 scanf("%d%d", &n, &m); 85 for(int i = 1; i <= n; ++i) 86 scanf("%lf", &a[i]); 87 Build(1, 1, n); 88 while(m--) { 89 int fl, x, y; 90 scanf("%d%d%d", &fl, &x, &y); 91 if(fl == 2) printf("%.4lf\n", Average(x, y)); 92 else if(fl == 3) printf("%.4lf\n", S2(x, y)); 93 else { 94 double k; 95 scanf("%lf", &k); 96 Update(1, 1, n, x, y, k); 97 } 98 } 99 }