zoukankan      html  css  js  c++  java
  • 洛谷P1471 方差

    题目背景

    滚粗了的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
    

    说明/提示

    样例说明:

    数据规模:

    思路:首先这个题与线段树板子题唯一不同的一点就是要求支持查询方差(平均数可以直接通过和除以区间长度得到),居然从黄题变成了蓝题(不可思议)。那么我们只需要看看多出一个查询方差需要维护什么东西就可以了。首先通过方差公式推导一波:将括号和求和公式拆开,能得到(设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;
    
  • 相关阅读:
    Fiddler的使用
    vue后台管理系统搭建
    有效的山脉数组
    从中序与后序遍历序列构造二叉树
    从前序与中序遍历序列构造二叉树
    最大二叉树
    填充每个节点的下一个右侧节点指针
    二叉树展开为链表
    翻转二叉树
    Java判断字符串是否为数字
  • 原文地址:https://www.cnblogs.com/57xmz/p/13228598.html
Copyright © 2011-2022 走看看