期待値の求め方
昇順に並び替えたときの 番目の値が である確率を とすると、
求めたい期待値は、
です。
与えられた数列の中で を満たす の個数を 個、 の個数を 個、 の個数を 個、 の個数を 個とします()。
個の を1以上以下の整数に置き換える場合の数は、 です。
このうち、番目の値が になるような置き換え方を考えます。 に置き換えられる個数を 個、 の個数を 個、 の個数を 個とします()。 このとき、昇順に並び替えると値が なのは、 番目から 番目までです。がこの範囲に含まれればよいので、
これを満たす の範囲を考えればよいです。あるについて考えると、個の中から個と個を選ぶ場合の数は、
で、への置き換えは1~ までの 通り、への置き換えは~ までの 通りあるので、場合の数は、
となります。これを条件を満たす の範囲で和を求めることで、昇順に並び替えたとき 番目の値が になる場合の数が求められ、これをで割ることで確率 が求められます。
この考え方では、のループでを求め、のループで答えを求める三重ループになりますが、実際には計算時間がかかりすぎます。
ループを減らす
これまで、番目の値がちょうどになる確率を考えていました。この発想を変えて、番目の値が以上になる確率を考えてみます。
この式から、
となります。求めたい期待値は、で表すと、
になります。
与えられた数列の中で を満たす の個数を 個、 の個数を 個、 の個数を 個とします()。
個の を1以上以下の整数に置き換える場合の数は、 です。
このうち、番目の値が 以上になるような置き換え方を考えます。 に置き換えられる個数を 個、 の個数を 個とします()。 このとき、昇順に並び替えると値が 以上になるためには、 であればよく、一重ループで済みます。
場合の数は、
となります。あとは、
から を求めることになります。
コード例 (Julia)
_MOD = 998244353 F = [] invF = [] function init_fact(N) push!(F,1) for i = 1:N push!(F,(F[i]*i)%_MOD) end for i = 1:N+1 push!(invF,invmod(F[i],_MOD)) end end function combination(N,A) if N < 0 || A > N || A < 0 return 0 end x = (F[N+1] * invF[A+1]) % _MOD x = (x * invF[N-A+1]) % _MOD return x end function solve() # 入力 readInts() = parse.(Int,split(readline())) readInt() = parse(Int,readline()) N,M,K = readInts() A = readInts() B = [] nzero = 0 for i = 1:N if A[i] > 0 push!(B,A[i]) else nzero += 1 # A_i = 0 の個数 end end # 階乗をあらかじめ計算しておく init_fact(nzero) # 0 以外の数字を昇順で並び替え sort!(B) ans = 0 for k = 1:M # K 番目の数字が k 以上になる確率 # K 番目の数字が k以上 <==> k より小さい数が K-1 個以下 kpos1 = searchsortedfirst(B,k) # k <= B[kpos1] # 「k より小さい数が K-1 個以下」 のうち、値が決まっている B を除いた個数 under_max = (K-1) - (kpos1-1) for under = 0:under_max C = combination(nzero, under) # nzero 個の中から under個を選ぶ選び方 n = 1 if k-1>=0 n = powermod( k-1, under, _MOD) # under個は、1~k-1 の中から自由に1つ選べる end m = 1 if M-k+1>=0 m = powermod( M-k+1, nzero-under, _MOD) # 残りは、k~M の中から自由に1つ選べる end s = (n * m) % _MOD s = (s * C) % _MOD ans += s ans = ans % _MOD end end ans = ans * invmod(powermod(M,nzero,_MOD), _MOD) ans = ans % _MOD println(ans) end # function solve # main solve()