洛谷[1471] 方差

  • 题目描述
    蒟蒻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)2i=lr(a2i+2×k×ai+k2)i=lra2i+2k×i=lrai+(rl+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 ;
}
点赞