洛谷 P1471 方差

传送门:洛谷 P1471 方差
算法分析:本题重在对方差的维护,平均值只要维护区间和
\(\bar{x}=\frac{\sum^n_{i=1}x_i}{n}\)
\(s^2=\frac{\sum^n_{i=1}(x_i-\bar{x})^2}{n}\)
\(\ \ \ \ =\frac{\sum^n_{i=1}(x_i^2-2x_i\bar{x}+\bar{x}^2)}{n}\)
\(\ \ \ \ =\frac{\sum^n_{i=1}x_i^2-\sum^n_{i=1}2x_i\bar{x}+\sum^n_{i=1}\bar{x}^2)}{n}\)
\(\ \ \ \ =\frac{\sum^n_{i=1}x_i^2-\sum^n_{i=1}2x_i\bar{x}+\sum^n_{i=1}\bar{x}^2)}{n}\)
\(\ \ \ \ =\frac{\sum^n_{i=1}x_i^2}{n}-\frac{\sum^n_{i=1}x_i\times2n\bar{x}}{n}+\frac{n\bar{x}^2}{n}\)
\(\ \ \ \ =\frac{\sum^n_{i=1}x_i^2}{n}-2\bar{x}^2+\bar{x}^2\)
\(\ \ \ \ =\frac{\sum^n_{i=1}x_i^2}{n}-\bar{x}^2\)
显然,只要维护区间平方和即可计算出方差\(s^2\)
那么如何维护区间平方和呢?
由完全平方公式,若把 \(x^2\) 更新为 \((x+\omega)^2\) ,则加上\((x+\omega)^2-x^2=x^2+2x\omega+\omega^2-x^2=2x\omega+\omega^2\)且优先进行平方的维护


#include<iostream>
#include<cstdio>
#include<cmath>
#define in(x) x=read()
#define mid ((l+r)>>1)
#define ls k<<1
#define rs k<<1 | 1
const int maxN=100000;
typedef int rd;
double v[4*maxN+1],sum1[4*maxN+1],sum2[4*maxN+1],z;
int n,m,x,y,op;
inline rd read();
void update(int,int,int,int,int,double);
double query_1(int,int,int,int,int);
double query_2(int,int,int,int,int);
void pushup(int),build(int,int,int);
void pushdown(int,int,int);
int main()
{
    in(n); in(m); build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        in(op); in(x); in(y);
        switch(op)
        {
            case 1:scanf("%lf",&z); update(1,1,n,x,y,z); break;
            case 2:printf("%.4lf\n",query_1(1,1,n,x,y)/(y-x+1)); break;
            case 3:
                double sum_1=query_2(1,1,n,x,y)/(y-x+1);
                double sum_2=query_1(1,1,n,x,y)/(y-x+1);
                printf("%.4lf\n",sum_1-sum_2*sum_2);
        }
    }
    return 0;
}
void build(int k,int l,int r)
{
    if(l==r)
    {
        scanf("%lf",&sum1[k]);
        sum2[k]=sum1[k]*sum1[k];
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(k);
}
void pushdown(int k,int l,int r)
{
    sum2[ls]+=2*v[k]*sum1[ls]+(mid-l+1)*v[k]*v[k];
    sum2[rs]+=2*v[k]*sum1[rs]+(r-mid)*v[k]*v[k];
    sum1[ls]+=(mid-l+1)*v[k];
    sum1[rs]+=(r-mid)*v[k];
    v[ls]+=v[k];
    v[rs]+=v[k];
    v[k]=0;
}
double query_1(int k,int l,int r,int ql,int qr)
{
    if(ql<=l && r<=qr) return sum1[k];
    if(v[k]) pushdown(k,l,r);
    double ans=0;
    if(ql<=mid) ans+=query_1(ls,l,mid,ql,qr);
    if(qr>mid) ans+=query_1(rs,mid+1,r,ql,qr);
    return ans;
}
double query_2(int k,int l,int r,int ql,int qr)
{
    if(ql<=l && r<=qr) return sum2[k];
    if(v[k]) pushdown(k,l,r);
    double ans=0;
    if(ql<=mid) ans+=query_2(ls,l,mid,ql,qr);
    if(qr>mid) ans+=query_2(rs,mid+1,r,ql,qr);
    return ans;
}
void update(int k,int l,int r,int ql,int qr,double w)
{
    if(ql<=l && r<=qr)
    {
        sum2[k]+=2*w*sum1[k]+w*w*(r-l+1);
        sum1[k]+=w*(r-l+1);
        v[k]+=w;
        return;
    }
    if(v[k]) pushdown(k,l,r);
    if(ql<=mid) update(ls,l,mid,ql,qr,w);
    if(qr>mid) update(rs,mid+1,r,ql,qr,w);
    pushup(k);
}
void pushup(int k)
{
    sum1[k]=sum1[ls]+sum1[rs];
    sum2[k]=sum2[ls]+sum2[rs];
}
inline rd read()
{
    rd num=0,f=1;
    char ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    if(ch=='-') {ch=getchar(); f=-1;}
    while(ch>='0' && ch<='9')
    {
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}
posted @ 2019-02-10 10:59  常青藤的花语  阅读(185)  评论(0编辑  收藏  举报

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。