[洛谷5142]区间方差 题解

前言

这是一道线段树板子题。

题解

我们观察这个式子

\[avg=\frac{1}{n}\sum_{i=1}^n a_i \]

\[d=\frac{1}{n}\sum_{i=1}^n (a_i-avg)^2 \]

我们把它展开变成

\[d=\frac{1}{n}\sum_{i=1}^n (a_i^2-2\times a_i \times avg - avg^2) \]

提出常数项得

\[d=\frac{1}{n}(\sum_{i=1}^n a_i^2-2\times avg \times \sum_{i=1}^n a_i + n\times avg^2) \]

那么我们开两棵线段树,一棵维护\(a_i\)的和,一棵维护\(a_i^2\)的和,在加一个乘法逆元即可。
结果蒟蒻博主还WA了一次,原因是取模不勤

代码

两棵线段树放在一起处理,十分方便。
逆元按照巨佬的说法,用了快速幂(再见拓欧)。

#include <cstdio>
#define ll long long

ll s[400005], s2[400005];
const ll MOD = 1e9 + 7;

ll read(){
	ll x = 0; int zf = 1; char ch = ' ';
	while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if (ch == '-') zf = -1, ch = getchar();
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}

ll getInv(ll x){
	ll res = 1;
	for (int y = MOD - 2; y; y >>= 1, x = (x * x) % MOD)
		if (y & 1)
			(res *= x) %= MOD;
	return res;
}

void build(int pos, int l, int r){
	if (l == r){
		s[pos] = read(); s2[pos] = (s[pos] * s[pos]) % MOD;
		return ;
	}
	int mid = (l + r) >> 1;
	build(pos << 1, l, mid);
	build(pos << 1 | 1, mid + 1, r);
	s[pos] = s[pos << 1] + s[pos << 1 | 1];
	s2[pos] = s2[pos << 1] + s2[pos << 1 | 1];
}

ll query(int pos, int l, int r, int x, int y){
	if (x <= l && r <= y)
		return s[pos];
	ll ans = 0; int mid = (l + r) >> 1;
	if (x <= mid)
		ans += query(pos << 1, l, mid, x, y);
	if (mid < y)
		(ans += query(pos << 1 | 1, mid + 1, r, x, y)) %= MOD;
	return ans;
}

ll query2(int pos, int l, int r, int x, int y){
	if (x <= l && r <= y)
		return s2[pos];
	ll ans = 0; int mid = (l + r) >> 1;
	if (x <= mid)
		ans += query2(pos << 1, l, mid, x, y);
	if (mid < y)
		(ans += query2(pos << 1 | 1, mid + 1, r, x, y)) %= MOD;
	return ans;
}

void modify(int pos, int l, int r, int x, ll val){
	if (l == r){
		s[pos] = val, s2[pos] = (val * val) % MOD;
		return ;
	}
	int mid = (l + r) >> 1;
	if (x <= mid)
		modify(pos << 1, l, mid, x, val);
	else if (mid < x)
		modify(pos << 1 | 1, mid + 1, r, x, val);
	s[pos] = (s[pos << 1] + s[pos << 1 | 1]) % MOD;
	s2[pos] = (s2[pos << 1] + s2[pos << 1 | 1]) % MOD;
}

int main(){
	int n = read(), m = read();
	build(1, 1, n); ll c, a, b;
	while (m--){
		c = read(), a = read(), b = read();
		if (c == 1)
			modify(1, 1, n, a, b);
		else if (c == 2){
			ll ans2 = query2(1, 1, n, a, b), ans = query(1, 1, n, a, b);
			ll avg = (ans * getInv(b - a + 1)) % MOD;
			ll res = (((ans2 - (avg * ans * 2ll % MOD) + ((((avg * avg) % MOD) * (b - a + 1)) % MOD)) % MOD * getInv(b - a + 1))) % MOD;
			while (res < 0) res += MOD;
			printf("%lld\n", res);
		}
	}
	return 0;
}
posted @ 2019-07-03 19:53  LinZhengmin  阅读(283)  评论(0编辑  收藏  举报

Contact with me