Luogu_P1471
算法
- 线段树
- 数学推导(平均数,方差)
思路
区间操作,很容易想到线段树。
但是方差不好合并,考虑拆解:
\[\large
\begin{aligned}
s^2 &= \frac{1}{n} \sum_{i = 1}^{n} (A_i - \overline A)^2 \\
&= \frac{1}{n} \sum_{i = 1}^{n} (A_i^2 - 2 \cdot A_i^2 \cdot \overline A + \overline A^2) \\
&= \frac{1}{n} (\sum_{i = 1}^{n} A_i^2 - \sum_{i = 1}^{n} 2 \cdot A_i^2 \cdot \overline A + \sum_{i = 1}^{n} \overline A^2) \\
&= \frac{1}{n} (\sum_{i = 1}^{n} A_i^2 - 2 \cdot \overline A \cdot \sum_{i = 1}^{n} A_i + n \overline A^2)
\end{aligned}
\]
所以我们只需要维护区间和 \(sum\),区间平方和 \(sum\_square\) 就可以了。
对于区间加的操作,有如下推导(记 \(sum2\) 为区间平方和):
\[\large
\begin{aligned}
sum2^{\prime} &= \sum_{i = 1}^{n} (A_i + val)^2 \\
&= \sum_{i = 1}^{n} (A_i^2 + 2 \cdot A_i \cdot val + val^2) \\
&= \sum_{i = 1}^{n} A_i^2 + \sum_{i = 1}^{n} 2 \cdot A_i \cdot val + \sum_{i = 1}^{n} val^2 \\
&= sum2 + 2 \cdot val \cdot \sum_{i = 1}^{n} + n \cdot val^2
\end{aligned}
\]
然后我们就做完了。
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 5;
long double arr[N], k;
int n, m, op_type, l, r;
struct SegmentTree {
public:
struct Node {
int l, r;
long double sum_square, sum, add;
};
Node tr[N << 2];
void lazy(int k, long double value) {
tr[k].sum_square += tr[k].sum * 2 * value + (tr[k].r - tr[k].l + 1) * value * value;
tr[k].sum += (tr[k].r - tr[k].l + 1) * value;
tr[k].add += value;
}
void push_up(int k) {
int lc = k << 1, rc = k << 1 | 1;
tr[k].sum_square = tr[lc].sum_square + tr[rc].sum_square;
tr[k].sum = tr[lc].sum + tr[rc].sum;
}
void push_down(int k) {
if (tr[k].add == 0) {
return void();
}
int lc = k << 1, rc = k << 1 | 1;
lazy(lc, tr[k].add);
lazy(rc, tr[k].add);
// ! 注意下传懒标记要清空。
tr[k].add = 0;
}
void build_tree(int k, int l, int r) {
tr[k].l = l, tr[k].r = r;
tr[k].sum = tr[k].sum_square = tr[k].add = 0;
if (tr[k].l == tr[k].r) {
tr[k].sum = arr[l];
tr[k].sum_square = arr[l] * arr[l];
return void();
}
int mid = (tr[k].l + tr[k].r) >> 1;
int lc = k << 1, rc = k << 1 | 1;
build_tree(lc, l, mid);
build_tree(rc, mid + 1, r);
push_up(k);
}
void modify(int k, int l, int r, long double value) {
if (tr[k].l >= l && tr[k].r <= r) {
lazy(k, value);
return void();
}
push_down(k);
int mid = (tr[k].l + tr[k].r) >> 1;
int lc = k << 1, rc = k << 1 | 1;
if (r <= mid) {
modify(lc, l, r, value);
} else if (l > mid) {
modify(rc, l, r, value);
} else {
modify(lc, l, mid, value);
modify(rc, mid + 1, r, value);
}
push_up(k);
}
long double __query_sum(int k, int l, int r) {
if (tr[k].l >= l && tr[k].r <= r) {
return tr[k].sum;
}
push_down(k);
int mid = (tr[k].l + tr[k].r) >> 1;
int lc = k << 1, rc = k << 1 | 1;
if (r <= mid) {
return __query_sum(lc, l, r);
} else if (l > mid) {
return __query_sum(rc, l, r);
} else {
return __query_sum(lc, l, mid) + __query_sum(rc, mid + 1, r);
}
}
long double __query_sum_square(int k, int l, int r) {
if (tr[k].l >= l && tr[k].r <= r) {
return tr[k].sum_square;
}
push_down(k);
int mid = (tr[k].l + tr[k].r) >> 1;
int lc = k << 1, rc = k << 1 | 1;
if (r <= mid) {
return __query_sum_square(lc, l, r);
} else if (l > mid) {
return __query_sum_square(rc, l, r);
} else {
return __query_sum_square(lc, l, mid) + __query_sum_square(rc, mid + 1, r);
}
}
long double query_average(int l, int r) {
long double sum = __query_sum(1, l, r);
return sum / (long double) (r - l + 1);
}
long double query_variance(int l, int r) {
long double average = query_average(l, r);
long double sum = __query_sum(1, l, r);
long double sum_square = __query_sum_square(1, l, r);
int length = r - l + 1;
// cout << average << " " << sum << " " << sum_square << '\n';
return (sum_square - 2 * average * sum + length * average * average) / (long double) length;
}
} segment_tree;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> arr[i];
}
segment_tree.build_tree(1, 1, n);
while (m --) {
cin >> op_type >> l >> r;
if (op_type == 1) {
cin >> k;
segment_tree.modify(1, l, r, k);
} else if (op_type == 2) {
long double result = segment_tree.query_average(l, r);
result = round(result * 10000) / (long double) 10000;
cout << setiosflags(ios::fixed) << setprecision(4) << result << '\n';
} else if (op_type == 3) {
long double result = segment_tree.query_variance(l, r);
result = round(result * 10000) / (long double) 10000;
cout << setiosflags(ios::fixed) << setprecision(4) << result << '\n';
}
}
return 0;
}
本文来自博客园,作者:Fallen_Leaf,转载请注明原文链接:https://www.cnblogs.com/FallenLeaf/p/18978601