Luogu_P1471

Luogu

算法

  • 线段树
  • 数学推导(平均数,方差)

思路

区间操作,很容易想到线段树。
但是方差不好合并,考虑拆解:

\[\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;
}
posted @ 2025-07-11 12:54  Fallen_Leaf  阅读(7)  评论(0)    收藏  举报