P1471 方差
算法
- 线段树
- 数学推导(平均数,方差)
思路
区间操作,很容易想到线段树。 但是方差不好合并,考虑拆解:
所以我们只需要维护区间和 ,区间平方和 就可以了。 对于区间加的操作,有如下推导(记 为区间平方和):
然后我们就做完了。
代码
#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;}
Comments
Quiet notes for this article.