Official

Ex - Rating Estimator Editorial by en_translator


First of all, let’s simplify what we evaluate. Let \(p_i\) be the estimated performance of contest \(i\) at some point. Here, let \(s\) be the minimum \(n\) such that

\[\frac{1}{n} \left(\sum_{i=1}^n p_i\right) \geq B.\]

(If there is no such \(n\), let \(s=N\).) Then the last change of the rating occurs at contest \(s\). Thus, the original problem is solved by finding \(s\) above and \(\sum_{i=1}^s p_i\).
A deformation yields

\[ \begin{aligned} &\frac{1}{n} \left(\sum_{i=1}^n p_i\right) \geq B \\ &\iff \sum_{i=1}^n p_i \geq nB \\ &\iff \sum_{i=1}^n (p_i - B) \geq 0. \end{aligned} \]

Using \(q_i = p_i - B\), we can thus say that \(s\) equals

  • the minimum \(n\) such that the \(n\)-element prefix sum of the sequence \((q_1, q_2, q_3, \dots, q_N)\) is \(0\) or greater (or \(s=N\) if there is no such \(n\)).

Therefore, we can solve the problem with a data structure supporting the following three queries fast enough:

  • Set \(q_c\) to \(x\).
  • Find \(s\) defined above.
  • Evaluate \(\sum_{i=1}^n q_n\).

There are several ways to process these queries. Here we explain how to put the maximum prefix sum on a segment tree.
Here is the detail. For a sequence \(B = (b_1, b_2, \dots, b_n)\), we define functions \(f(B)\) and \(g(B)\) by:

\[ \begin{aligned} f(B) &= b_1 + b_2 + \dots + b_n, \\ g(B) &= \max\lbrace b_1, b_1 + b_2, \dots, b_1 + b_2 + \dots + b_n \rbrace. \end{aligned} \]

Then, for two non-empty sequences \(S\) and \(T\) and their concatenation \(S+T\), we have:

\[ \begin{aligned} f(S + T) &= f(S) + f(T), \\ g(S + T) &= \max\lbrace g(S), f(S) + g(T). \rbrace \end{aligned} \]

Thus, the segment tree which stores tuples \((f(B), g(B))\) for each segment containing sequences \(B\) works well (if the identity element is defined appropriately). The queries of second type can be processed by performing binary search on the segment tree.
Hence, the problem can be solved in a total of \(\mathrm{O}(N + Q \log N)\) time, which is fast enough.

  • Sample code (C++)
#include <iomanip>
#include <iostream>
#include <utility>
#include <vector>
using namespace std;
#include "atcoder/segtree.hpp"

using T = pair<long long, long long>;
T f(T a, T b) { return {a.first + b.first, max(a.second, a.first + b.second)}; }
T ti() { return {0, -1e18}; }
using Seg = atcoder::segtree<T, f, ti>;

int main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);
  cout << fixed << setprecision(20);

  long long N, B, Q;
  cin >> N >> B >> Q;
  vector<long long> a(N);
  for (auto& x : a) cin >> x;

  vector<T> init(N);
  for (int i = 0; i < N; i++) init[i] = {a[i] - B, a[i] - B};
  Seg seg{init};

  while (Q--) {
    long long c, x;
    cin >> c >> x;
    --c;
    a[c] = x;
    seg.set(c, {x - B, x - B});
    auto i = seg.max_right(0, [](T v) { return v.second < 0; });
    if (i != N) i++;
    auto s = seg.prod(0, i).first;
    cout << 1.0 * (s + B * i) / i << "\n";
  }
}

posted:
last update: