公式

F - Random Gathering 解説 by en_translator


\(\lbrack L,R\rbrack\) will denote the set of integers between \(L\) and \(R\), inclusive.

Let us correspond \(\displaystyle\omega=(\omega _ 1,\omega _ 2,\ldots,\omega _ M)\in\Omega=\prod _ {i=1} ^ M\lbrack L _ i,R _ i\rbrack\) to a procedure where the integer \(\omega _ i\) is chosen for the \(i\)-th operation \((1\le i\le M)\). (Every \(\omega\) occurs with equal probability.) Define a random variable \(X _ {i,j}\ (0\le i\le M,1\le j\le N)\) on the sample space \(\Omega\) as the number of stones in plate \(j\) after the \(i\)-th operation. (For \(i=0\), define \(X _ {i,j}=A _ j\).)

What we want is the expected number of stones in each plate \(E\lbrack X _ {M,j}\rbrack\ (1\le j\le N)\) after the operations are completed.

For \(1\le i\le M\), \(X _ {i,j}\) satisfies:

  • \(X _ {i,j}=X _ {i-1,j}\) if \(j\notin\lbrack L _ i,R _i\rbrack\),
  • \(X _ {i,j}=0\) if \(j\in\lbrack L _ i,R _ i\rbrack\) and \(\omega _ i\ne j\),
  • \(\displaystyle X _ {i,j}=\sum _ {k=L _ i} ^ {R _ i}X _ {i - 1,k}\) if \(j\in\lbrack L _ i,R _ i\rbrack\) and \(\omega _ i=j\).

So we can evaluate \(X _ {i.j}\) as follows:

  • \(E\lbrack X _ {i,j}\rbrack=E\lbrack X _ {i-1,j}\rbrack\) if \(j\notin\lbrack L _ i,R _i\rbrack\)
  • \(\displaystyle E\lbrack X _ {i,j}\rbrack=\dfrac1{R _ i-L _ i+1}E\left\lbrack\sum _ {k=L _ i} ^ {R _ i}X _ {i-1,k}\right\rbrack=\dfrac1{R _ i-L _ i+1}\sum _ {k=L _ i} ^ {R _ i}E\lbrack X _ {i-1,k}\rbrack\) if \(j\in\lbrack L _ i,R _i\rbrack\) (by linearity of expected value)

Hence, the problem can be solved by managing \(E\lbrack X _ {i,j}\rbrack\ (1\le j\le N)\) in a segment tree that supports lazy segment tree that supports segment sum and segment update, and updating it in-line.

Here is sample code.

#include <iostream>
#include <optional>
#include <atcoder/lazysegtree>
#include <atcoder/modint>

int main() {
    using namespace std;
    using modint = atcoder::static_modint<998244353>;
    // Lazy segment tree that manages (segment length, segment sum)
    using segtree = atcoder::lazy_segtree<
        pair<unsigned, modint>,
        [](const auto& lhs, const auto& rhs){return make_pair(lhs.first + rhs.first, lhs.second + rhs.second);},
        []{return pair<unsigned, modint>{};},
        optional<modint>,
        [](const auto& a, const auto& b){return a ? make_pair(b.first, b.first * *a) : b;},
        [](const auto& a, const auto& b){return a ? a : b;},
        []{return nullopt;}
    >;

    unsigned N, M;
    cin >> N >> M;
    segtree expected_value(N);
    for (unsigned i{}, A; i < N; ++i) {
        cin >> A;
        expected_value.set(i, {1, A});
    }
    for (unsigned i{}, L, R; i < M; ++i) {
        cin >> L >> R;
        --L; // Convert to half-open segment & 0-based index
        const auto sum{expected_value.prod(L, R).second}; // find segment sum
        expected_value.apply(L, R, sum / (R - L)); // and bulk update
    }

    for (unsigned i{}; i < N; ++i)
        cout << expected_value.get(i).second.val() << " ";
    cout << endl;
    return 0;
}

投稿日時:
最終更新: