ABC231: G - Balls in Boxes
問題概要
から までの番号がついた 個の箱があり,箱 に入っているボールの数は である。 個の数から等確率で つ選んでボールを 足すという操作を 回 繰り返す。箱 に操作後に入っているボールの数を として の期待値はいくらか。
(問題文は https://atcoder.jp/contests/abc231/tasks/abc231_g に記載されている。)
制約
アイディア
あとで答えを で割ることにして,すべての操作における の和を求めることを考える。
まず重要なのは,各操作における の総和を求めることは,各操作を終えた後に 個の箱それぞれからボールを つずつ取り出す方法を数えるのと同じである。そこで,操作前に箱に入っていたボールを赤色に,操作で入れるボールを白色に塗ることにする。このとき,最後に選んだボールのうち赤色に塗られたものを取り出した箱の添字の集合を,白色に塗られたものを取り出した箱の添字の集合をとする。このときかつである。また,箱に入っている白色のボールの個数をとする。に関して以下が成り立つ。
この状況に限定した場合における「操作後に 個の箱それぞれからボールを つずつ取り出す方法」は である。また,このような状況になる場合の数は である。ところで,これらをかけ合わせると
となる。ここで,を固定した場合のすべての適切なの組み合わせについての総和をとする。が *1にのみ依存することを示す。
今, を
とおく。すると,はと書き直せる。また,であり,である。つまり,は,を満たすすべてのの組み合わせについてを求めその総和をとったものにをかけたものとなる。逆に,,を満たすすべてのの組み合わせについての総和を取るととなる。ところで,
より
となるが,のの係数がちょうどとなっている *2。のの係数を求めるにはを回微分してからを代入してで割ればよく,実際にやるととなる。よってなのでとなり,にのみ依存することが示された。
これによって
は
と書き換えられる。結局求めたいものはすべてのについてのこの総和である。ここではが同じものをまとめて計算することを考えると,が同じであるすべてのについての総和が求まればよく,これは簡単なのDPで可能である。
実装
まずを「個目までの数から個選ぶ際の選んだ数の積」の和としてこれを求める。初期状態はで,遷移は (ただしとする)となる。
また,も求めておく。, である。
そして を求め,最後にで割る。あるいは最初から を求めてもよいかもしれない。
コード
#include "bits/stdc++.h" using namespace std; const long long int MOD = 998244353LL; long long int dp[1001][1001],calc[1001],A[1000]; long long int pow_binary(long long int x, long long int y){ long long int ret = 1LL; while(y){ if(y%2){ ret *= x; ret %= MOD; } x *= x; x %= MOD; y /= 2LL; } return ret; } int main(void){ int N,i,j; long long int K,perK,V=1LL,ANS=0LL; cin >> N >> K; perK = pow_binary(N,MOD-2); for(i=0; i<N; ++i){ cin >> A[i]; A[i] %= MOD; } dp[0][0] = 1LL; calc[0] = 1; for(i=0; i<N; ++i){ calc[i+1] = (calc[i]*(K-i))%MOD; for(j=0; j<=i; ++j){ dp[i+1][j] += dp[i][j]; dp[i+1][j] %= MOD; dp[i+1][j+1] += (dp[i][j]*A[i])%MOD; dp[i+1][j+1] %= MOD; } } for(i=0; i<=N; ++i){ calc[i] *= dp[N][N-i]; calc[i] %= MOD; calc[i] *= V; calc[i] %= MOD; ANS += calc[i]; ANS %= MOD; V *= perK; V %= MOD; } cout << ANS << endl; return 0; }
感想
結構悩んで面白かった。分かる人からしたら基礎中の基礎なんだろうけど。