CF
July 18, 2023

CodeTON5 G - Tenzing and Random Operations

https://codeforces.com/contest/1842/problem/G
Очень крутая задача!

К сожалению, ее я сам придумать не смог, пришлось прочитать разбор. В ней, кажется, если не думать вообще про мат. ожидание, а просто считать что (Ответ = Сумма / Кол-во Вариантов), то придумать решение будет сложно.

Единственное, что я знаю про мат. ожидание это то, что E(a*b) = E(a) * E(b) если a и b независимые числа, тут это и применяется.

Заведем n*m рандомных переменных, где x[i][j] - добавка к a[i] на j-ом ходе. x[i][j] либо 0, либо v.

Тогда ответ = мат ожидание произведения (a[i] + sum(x[i])).

Давайте постараемся решить для n=2, ответ = (a[0] + sum(x[0])) * (a[1] + sum(x[1])). Посмотрим на мат. ожидание x[0][j] * x[1][j] для какого-то j.

  • Если x[0][j] = 0, то ответ просто 0
  • Иначе раз x[0][j] = v, то и x[1][j] = v.

Можно сделать вывод, что мат. ожидание = x[0][j] * v. Понятно, что это обобщается и для большего кол-ва индексов.

  • Мат ожидание x[i1][j] * x[i2][j] * x[i3][j] * ... * x[ik][j] = x[i1][j] * v**(k-1)

Теперь вспомним, что при разных a и b x[i][a] и x[i][b] это независимые друг от друга величины! Значит, нам достаточно посчитать мат ожидание произведения x_a и x_b и перемножить их.

Теперь хочется раскрыть скобки в нашем произведении (которое (a[i] + sum(x[i]))) и посчитать ответ.

Давайте тогда напишем dp[i][j] - мат ожидание ответа, когда мы рассмотрели первые i индексов, и среди них встретилось j различных x-ов (т.е. для x[_][k], считаем кол-во различных k-шек). Понятно, что j <= min(n, m), потому что в каждом индексе при раскрытии скобок мы выберем либо a[i], либо какой-то из уже добавленных x-ов, либо добавим ровно один.

Вот, из каждого состояния dp у нас ровно 3 перехода (выбрать a[i], выбрать уже добавленный x <=> умножить на v, добавить новый x), и это достаточно просто пишется.

int main() {
    ios::sync_with_stdio(false);    
    cin.tie(nullptr);    
     
    int n, m, v;    
    cin >> n >> m >> v;     
    
    int inv = power(n);    
    vector dp(n + 1, vector<int>(n + 1));    
    dp[0][0] = 1;     
    
    for (int i = 1; i <= n; ++i) {    
        int a;        
        cin >> a;        
        for (int j = 0; j < i; ++j) {        
            dp[i][j + 1] = (dp[i][j + 1] + 1LL * dp[i - 1][j] * v % MOD * (m - j) % MOD * i % MOD * inv) % MOD;            
            dp[i][j] = (dp[i][j] + dp[i - 1][j] * (a + 1LL * j * v % MOD)) % MOD;        
        }    
    }     
    
    int ans = accumulate(dp[n].begin(), dp[n].end(), 0LL) % MOD;     
    
    cout << ans << '\n';     
    
    return 0;
}

Всем спасибо за внимание! :)