P1471 方差

P1471 方差

分析

数据结构的题目,我们直接来分析操作。

操作 1:1 x y k ,表示将第 x 到第 y 项每项加上 kk 为一实数。
操作 2:2 x y ,表示求出第 x 到第 y 项这一子数列的平均数。
操作 3:3 x y ,表示求出第 x 到第 y 项这一子数列的方差。

操作一:

没什么好说的,区间加。加个懒标记add就好啦。

我们主要看的是,区间加的操作,对其余我们要维护的变量的改变

操作二:

我们首先确定的是,我们需要维护一个avg或者sum,我个人比较喜欢维护sum,这样精度损失会小一些,同时对于区间加而言,我们维护sum更习惯一些。

这样再算一个区间的平均值是,直接用sum/lenlen为区间长度)

操作三:

这题的重点是,如何求方差。但其实也不难,我们直接来推公式。

\[s^2 = \frac{\sum_{i=1}^{n}(a_i-\bar{a})^2}{n} \]

\[s^2 = \frac{\sum_{i=1}^{n}a_i^2-2\bar{a}\sum_{i=1}^{n}a_i+n\bar{a}^2}{n} \]

\[设sum1 = \sum_{i=1}^{n}a_i,sum2 = \sum_{i=1}^{n}a_i^2 \]

\[s^2 = \frac{sum2-\frac{2sum1^2}{n}+n(\frac{sum1}{n})^2}{n} \]

\[s^2 = \frac{sum2-\frac{sum1^2}{n}}{n} \]

则我们需要维护的就是sum1sum2

而区间加对两者的影响,我们只说对sum2的。

\[\sum_{i=1}^{n}(a_i+x)^2 \]

拆开

\[\sum_{i=1}^{n}a_i^2+2x\sum_{i=1}^{n}a_i+nx^2 \]

则,\(sum2_{变化后的}=sum2_{变化前的}+2xsum1_{变化前的}+nx^2\)

结束啦,直接看代码。

Ac_code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct Node
{
    int l,r;
    double sum1,sum2,tag;
}tr[N<<2];
double w[N];
int n,m;

void pushup(int u)
{
    tr[u].sum1 = tr[u<<1].sum1 + tr[u<<1|1].sum1;
    tr[u].sum2 = tr[u<<1].sum2 + tr[u<<1|1].sum2;  
}

void build(int u,int l,int r)
{
    if(l==r)
    {
        tr[u] = {l,r,w[l],w[l]*w[l],0};
        return ;
    }
    tr[u] = {l,r,0,0,0};
    int mid = l + r >> 1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(u);
}

void pushdown(int u)
{
    auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
    if(root.tag)
    {
        auto k = root.tag;
        left.sum2 += 2*k*left.sum1 + k*k*(left.r - left.l + 1);
        left.sum1 += (left.r - left.l + 1)*k;
        left.tag += k;
        right.sum2 += 2*k*right.sum1 + k*k*(right.r - right.l + 1);
        right.sum1 += (right.r - right.l + 1)*k;
        right.tag += k;
        root.tag = 0;
    }
}

void modify(int u,int l,int r,double k)
{
    if(l<=tr[u].l&&tr[u].r<=r)
    {
        tr[u].sum2 += 2*k*tr[u].sum1 + k*k*(tr[u].r - tr[u].l + 1); 
        tr[u].sum1 += (tr[u].r - tr[u].l + 1)*k;
        tr[u].tag += k;
        return ;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    if(l<=mid) modify(u<<1,l,r,k);
    if(r>mid) modify(u<<1|1,l,r,k);
    pushup(u);
}

double querysum1(int u,int l,int r)
{
    if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum1;
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    double res = 0;
    if(l<=mid) res += querysum1(u<<1,l,r);
    if(r>mid) res += querysum1(u<<1|1,l,r);
    return res;
}

double querysum2(int u,int l,int r)
{
    if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum2;
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    double res = 0;
    if(l<=mid) res += querysum2(u<<1,l,r);
    if(r>mid) res += querysum2(u<<1|1,l,r);
    return res;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lf",w+i);
    build(1,1,n);
    while(m--)
    {
        int op,x,y;scanf("%d%d%d",&op,&x,&y);
        double k;
        if(op==1)
        {
            scanf("%lf",&k);
            modify(1,x,y,k);
        }
        else if(op==2) printf("%.4lf\n",querysum1(1,x,y)/(y-x+1));
        else 
        {
            double sum1 = querysum1(1,x,y)/(y-x+1),sum2 = querysum2(1,x,y)/(y-x+1);
            double ans = sum2-sum1*sum1;
            printf("%.4lf\n",ans);
        }
    }
    return 0;
}
posted @ 2022-05-07 21:41  艾特玖  阅读(79)  评论(0)    收藏  举报