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;
}
投稿日時:
最終更新: