P5142 区间方差

同但更难: P1471 方差 - 洛谷

题目描述

现在给定一个长度为 n 的序列 b1,b2bn。你需要支持两种操作。每种操作的格式为 c x y

c=1,为修改操作,代表将 bx 赋值为 y

c=2,为查询操作,代表查询 bxby 的方差。

为了避免浮点数误差,请以分数取模形式输出结果(对 (109+7)取模)。

1n,m1×1051bi1×1091xn。对于操作 1,1y1×109。对于操作 2,xyn

输入格式

第一行两个整数 n,m,代表序列 b 的长度为 n,有 m 个操作。

第二行 n 个整数 bi,表示序列 b 的初始值。

下面有 m 行整数,每行格式为 c x y,含义如上文所示。保证所有操作合法。

输出格式

对于每个操作 2,输出一行。

Solution

数学/树状数组/线段树

可以将求该区间的方差进行化简:
d=1ni=1n(aia)2=1n(ai22×ai×a+a2)=1nai22nai×a+1na2

1n(ai22×a×n×a+n×a2)=1nai2a2

(n×a=ai)

d=1nai2(ain)2

用树状数组则需要维护区间和,区间平方和两个树状数组。
(待更 )

#define int long long
#define lowbit(x) x&(-x)
const int mod = 1e9 + 7;
int n, m, tr1[100010], tr2[100010];
int qpow(int a) {
    int b = mod - 2, ans = 1;
    while (b) {
        if (b & 1)ans = (a * ans) % mod;
        a = (a * a) % mod;b >>= 1;
    }
    return ans;
}
void add(int tr[], int x, int k) {
    while (x <= n)tr[x] = (tr[x] + k) % mod, x += lowbit(x);
}
int query(int tr[], int x) {
    int ans = 0;
    while (x)ans = (ans + tr[x]) % mod, x -= lowbit(x);
    return ans % mod;
}
void solve() {
    cin >> n >> m;
    for (int i = 1;i <= n;i++) {
        int x;cin >> x;
        add(tr1, i, x % mod);
        add(tr2, i, (x * x) % mod);
    }
    while (m--) {
        int op, x, y;cin >> op >> x >> y;
        if (op == 1) {
            int p1 = (query(tr1, x) - query(tr1, x - 1) + mod) % mod;
            int p2 = (query(tr2, x) - query(tr2, x - 1) + mod) % mod;
            add(tr1, x, (y - p1) % mod);
            add(tr2, x, (y * y - p2 + mod) % mod);
        } else {
            int sum = (query(tr1, y) - query(tr1, x - 1) + mod) % mod;
            int sqsum = (query(tr2, y) - query(tr2, x - 1) + mod) % mod;
            int inv = qpow(y - x + 1);
            int ans1 = (sum * inv) % mod;
            int ans2 = (sqsum * inv) % mod;
            int ans = ans2 % mod - (ans1 * ans1) % mod;
            cout << (ans + mod) % mod << '\n';
        }
    }
}