洛谷 P1471 方差

Posted Last_order

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P1471 方差相关的知识,希望对你有一定的参考价值。

题目链接

区间加,区间查询,显然的线段树 & 分块
说实话第一眼看到这个题的时候我是很懵的:
线段树每个区间要维护什么呢?

我们定义 \\(Sum = \\sum\\limits_{i=1}^n A_i = A_1 + A_2 + \\ldots + A_n\\)
\\(\\qquad \\ \\ \\; \\; \\; Pow = \\sum\\limits_{i=1}^n A_i^2 = A_1^2 + A_2^2 + \\ldots + A_n^2\\)

还要知道平均数的公式 \\(\\overline{A} = \\cfrac{Sum}{n}\\)

先把方差的计算公式展开一下:

\\[\\begin{align*} \\large s^2 &= \\cfrac{1}{n} \\sum_{i=1}^n\\left(A_i - \\overline{A}\\right)^2\\\\ &= \\cfrac{ \\left(A_{1} - \\overline{A} \\right)^2 + \\left(A_{2} - \\overline{A} \\right)^2 + \\ldots + \\left(A_{n} - \\overline{A} \\right)^2}{n} \\\\ &= \\cfrac{\\left(A_1^2 + A_2^2 + \\ldots + A_n^2\\right) - 2\\overline{A} \\times \\left(A_1 + A_2 + \\ldots + A_n \\right) + n \\times \\overline{A}^2}{n}\\\\ &= \\cfrac{Pow - 2\\overline{A} \\times Sum + n \\times \\overline{A}^2}{n}\\\\ &= \\cfrac{Pow}{n} - 2 \\overline{A}^2 + \\overline{A}^2\\\\ &= \\cfrac{Pow}{n} - \\cfrac{Sum^2}{n^2} \\end{align*}\\]

而上面的式子可以由 \\(Sum\\)\\(Pow\\) 求得(\\(n\\) 为区间长度),所以我们只要维护每个区间的区间和以及平方和即可。(●\'◡\'●)

然后我们再来求一下区间修改的式子。

若是给 \\(A_1\\)\\(A_n\\) 这段区间加上有个数 \\(k\\),区间和比较简单就不列式子了,区间平方和就为:

\\[\\begin{align*} Ans &= (A_1 + k)^2 + (A_{2} + k)^2 + \\ldots + (A_{n} + k)^2 \\\\ &= A_1^2 + A_{2}^2 + \\ldots + A_n^2 + 2k(A_1 + A_2 + \\ldots + A_n) + n * k^2\\\\ &= Pow + 2k \\cdot Sum + n \\cdot k^2\\\\ &= Pow + k \\cdot (2 \\times Sum + n \\times k) \\quad \\text{(这步单纯为了让式子好看一点)} \\end{align*}\\]

然后我们就得到了这道题所要用的所有公式,只需将 \\(1 \\sim n\\) 这个区间推广到题目给的区间 \\(l \\sim r\\) 即可。

于是我们的pushdown(下传懒标记)和add(区间加)函数就很好写了:

inline void add(int l, int r, double k, int node){
    if(l <= t[node].l && t[node].r <= r){
        t[node].lazy += k;
        t[node].pow += (k * t[node].sum * 2) + k * k * (t[node].r - t[node].l + 1); //乘 2 不能用位运算代替,因为是浮点型的;
        t[node].sum += (t[node].r - t[node].l + 1) * k;
        return;
    }
    if(t[node].lazy) pushdown(node);
    int mid = t[node].l + t[node].r >> 1;
    if(l <= mid) add(l, r, k, node << 1);
    if(mid < r) add(l, r, k, node << 1 | 1);
    update(node);
}

inline void pushdown(int node){  //看似很长,其实就是按照上面的公式来的;
    t[node << 1].lazy += t[node].lazy;
    t[node << 1 | 1].lazy += t[node].lazy;
    t[node << 1].pow += (t[node << 1].sum * t[node].lazy * 2) + (t[node << 1].r - t[node << 1].l + 1) * t[node].lazy * t[node].lazy;
    t[node << 1].sum += (t[node << 1].r - t[node << 1].l + 1) * t[node].lazy;
    t[node << 1 | 1].pow += (t[node << 1 | 1].sum * t[node].lazy * 2) + (t[node << 1 | 1].r - t[node << 1 | 1].l + 1) * t[node].lazy * t[node].lazy;
    t[node << 1 | 1].sum += (t[node << 1 | 1].r - t[node << 1 | 1].l + 1) * t[node].lazy;
    t[node].lazy = 0;
}

其中每个节点维护的sumpow与上文中意义一致。

for(int i = 1; i <= m; i++){
    scanf("%d %d %d", &check, &x, &y);
    if(check == 1){
        scanf("%lf", &k);
        add(x, y, k, 1);
    }
    double Average = ask_sum(x, y, 1) / (y - x + 1); //这里的 Average 表示区间的平均值,用变量记录一下减少重复运算;
    if(check == 2){
        printf("%.4lf\\n",  Average);
    }
    if(check == 3){
        double Pow = ask_pow(x, y, 1);
	printf("%.4lf\\n",  Pow / (y - x + 1) - (Average * Average));
    }
}

由于比较长就在这不放全部代码了,剩下的部分都是线段树的基本操作。
还有唯一一点要注意的就是题目中给的数列为实数,不一定是整数,存数列的数组一定要开double啊!!!


至于文章开头提到的分块太麻烦不想写了

以上是关于洛谷 P1471 方差的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1471 方差

洛谷 P1471 方差

[luogu]P1471 方差

P1471 方差

Luogu P1471 方差

[题解]洛谷比赛『期末考后的休闲比赛2』