[lnsyoj282/luoguP2122]还教室

题意

原题链接
给定序列a,要求处理区间加,区间查平均值,区间查方差

sol

显然线段树
区间加操作请参考[lnsyoj281/luoguP2023/AHOI2009]维护序列

区间查平均值

由于$$\overline a = \frac{\sum_{i=1}^{n} a_i}{n}$$
所以只需记录区间和即可

区间查方差

由于$$S^2 = \frac{\sum_{i=1}^{n} (a_i - \overline a)^2}{n}$$
可得$$S^2 = \frac{\sum_{i=1}^{n} a_i^2 - \sum_{i=1}^{n} 2 \cdot \overline a \cdot a_i + n \cdot \overline a}{n}$$
其中,\(\sum_{i=1}^{n} a_i\)\(\sum_{i=1}^{n} a_i^2\)均能使用线段树处理,而\(\overline a\)的处理方式前面已经处理完毕,因此该式可以计算出来

区间查平方和

下面介绍\(\sum_{i=1}^{n} a_i^2\)的处理方式:
假设序列\(a\)的每个值都加上\(x\),则平方和的值由$$\sum_{i=1}^{n} a_i^2$$
变为$$\sum_{i=1}^{n} a_i^2 - \sum_{i=1}^{n} 2 \cdot x \cdot a_i + n \cdot x$$
因此,只需要在处理区间和之前,处理区间平方和即可

代码

注:因为蒟蒻懒得想为了更方便编写题解,蒟蒻在代码中单独编写了分数(Fraction)结构体,详见代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
typedef __int128 LL;

const int N = 100005;

int n, m;
LL lazyadd[N * 4];
int a[N];

struct Node{
    LL sum = 0, square = 0;
}tree[N * 4];

LL lcm(LL x, LL y){
    return x / __gcd(x, y) * y;
}

struct Fraction{ //分数结构体
    LL x, y;

    Fraction(LL x, LL y):x(x), y(y){}

    void divide(){ //约分
        if (!x) {
            y = 1;
            return ;
        }
        LL d = __gcd(x, y);
        x /= d, y /= d;
    }

    Fraction operator-() const{
        Fraction copy(-x, y);
        return copy;
    }

    Fraction operator+(const Fraction &W) const{
        LL yy = lcm(y, W.y);
        LL atimes = yy / y, btimes = yy / W.y;
        LL xx = x * atimes + W.x * btimes;
        Fraction ans(xx, yy);
        ans.divide();
        return ans;
    }

    Fraction operator-(const Fraction &W) const{
        Fraction copy(x, y);
        return copy + (-W);
    }

    Fraction operator*(const Fraction &W) const{
        Fraction ans(x * W.x, y * W.y);
        ans.divide();
        return ans;
    }

    Fraction operator*(const LL &W) const{
        Fraction ans(x * W, y);
        ans.divide();
        return ans;
    }

    Fraction operator/(const LL &W) const{
        Fraction ans(x, y * W);
        ans.divide();
        return ans;
    }

    void print(){
        long long xx = x, yy = y;
        printf("%lld/%lld\n", xx, yy);
    }
};

void pushup(int u){
    tree[u].sum = tree[u << 1].sum + tree[u << 1 | 1].sum;
    tree[u].square = tree[u << 1].square + tree[u << 1 | 1].square;
}

void pushdown(int u, int l, int r){
    int mid = l + r >> 1;
    tree[u << 1].square += 2 * tree[u << 1].sum * lazyadd[u] + lazyadd[u] * lazyadd[u] * (mid - l + 1);
    tree[u << 1 | 1].square += 2 * tree[u << 1 | 1].sum * lazyadd[u] + lazyadd[u] * lazyadd[u] * (r - mid);
    tree[u << 1].sum += lazyadd[u] * (mid - l + 1);
    tree[u << 1 | 1].sum += lazyadd[u] * (r - mid);
    lazyadd[u << 1] += lazyadd[u];
    lazyadd[u << 1 | 1] += lazyadd[u];
    lazyadd[u] = 0;
}

void build(int u, int l, int r){
    if (l == r) {
        tree[u].sum = a[l];
        tree[u].square = a[l] * a[l];
        return ;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void update(int u, int l, int r, int L, int R, int val){
    if (L <= l && r <= R){
        lazyadd[u] += val;
        tree[u].square += 2 * tree[u].sum * val + val * val * (r - l + 1);
        tree[u].sum += val * (r - l + 1);
        return ;
    }
    pushdown(u, l, r);
    int mid = l + r >> 1;
    if (L <= mid) update(u << 1, l, mid, L, R, val);
    if (R > mid) update(u << 1 | 1, mid + 1, r, L, R, val);
    pushup(u);
}

LL query(int u, int l, int r, int L, int R, int op){ //op=0时,为查询区间和,op=1时,为查询区间平方和
    if (L <= l && r <= R){
        if (op) return tree[u].square;
        return tree[u].sum;
    }
    pushdown(u, l, r);
    LL res = 0, mid = l + r >> 1;
    if (L <= mid) res += query(u << 1, l, mid, L, R, op);
    if (R > mid) res += query(u << 1 | 1, mid + 1, r, L, R, op);
    return res;
}

int main(){
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    build(1, 1, n);

    while (m -- ){
        int op;
        int l, r, k;
        scanf("%d%d%d", &op, &l, &r);
        // puts("");
        if (op == 1) {
            scanf("%d", &k);
            update(1, 1, n, l, r, k);
        }
        else {
            LL sum = query(1, 1, n, l, r, 0), cnt = r - l + 1;
            Fraction average(sum, cnt); //平均值
            average.divide();
            if (op == 2) average.print();
            else {
                LL Square = query(1, 1, n, l, r, 1);
                Fraction square(Square, 1);
                Fraction varience(1, cnt);
                varience = varience * (square - average * 2 * sum + average * average * (r - l + 1)); //方差
                varience.divide();
                varience.print();
            }
        }
    }
}
posted @ 2024-05-22 18:39  是一只小蒟蒻呀  阅读(12)  评论(0编辑  收藏  举报