実際には少なく見積もれる計算量がなかなか慣れない。
問題概要
人をいくつかのチームに分ける。
この時、番目の人は人以下のチームのみに所属できる。
この条件の元、人のチーム分けとして何通り考えられるかをMOD で求めよ。
(ここでは、人は区別するがチームは区別しない)
制約
解法
dpの方針まではわかったけど、計算量がうまく落とせず解けなかった...。
公式解説ベース。
まず、人が属するチームを作る場合を考える。
この時、以下のことが分かる。
- となる人はの値に関係なく所属可能
- となる人は所属不可能
そこで、以下のようなdpを考える。
となる人の中から、人以上を含むいくつかのチームを作った時点で人余った時の通り数
このdpの初期値をとしてから、を問題の解として求めればよい。
そして、値の伝搬であるが、人存在した時に人チームをチーム作る時の通り数は
となる。
そのため、とした時、(はとなる人数)
と伝搬すればよい。
あとは実際に計算するだけだが、一見するとこの計算はっぽく見える。
ここで、からへ伝搬させる計算の回数は回()であることから、
における伝搬の合計回数の上限は
と評価できるため、計算量は となる。
と評価できるのよく見落とすので慣れたい。
実装
提出コード(PyPy3): Submission #4511779 - Mujin Programming Challenge 2018
import sys readline = sys.stdin.readline N = int(readline()) *A, = map(int, readline().split()) B = [0]*(N+1) for a in A: B[a] += 1 MOD = 998244353 fact = [1]*(N+1) rfact = [1]*(N+1) r = 1 for i in range(1, N+1): fact[i] = r = r * i % MOD rfact[i] = pow(r, MOD-2, MOD) dp = [0]*(N+1) dp2 = [0]*(N+1) zeros = [0]*(N+1) dp[0] = 1 for i in range(N, 0, -1): c = B[i] dp2[:] = zeros for j in range(N-c+1): num = j + c # v == pow(rfact[i], k, MOD) ri = rfact[i]; v = 1 for k in range(num//i+1): dp2[num - k*i] += dp[j] * fact[num] * rfact[num - k*i] * rfact[k] * v % MOD v = v * ri % MOD dp, dp2 = dp2, dp sys.stdout.write("%d\n" % (dp[0] % MOD))