phyllo’s algorithm note

レッドコーダーへの道のりは遠い。休んでる場合じゃない!

ARC067 E. Grouping

問題

1からNまで番号がついたN人がいる。

  • どのグループも、そのグループに含まれる人数がA人以上B人以下
  • i人のグループの数をF_iとしたとき、F_iは0またはC以上D以下

このようなグループ分けが何通りあり得るかmod 10^9+7で求めよ。

制約

  • 1 <= N <= 10^3
  • 1 <= A <= B <= N
  • 1 <= C <= D <= N

解法

解説、解説放送の方法。


問題がややイメージしにくいので、具体事例を考えてみる。
例えば、「(1),(2),(5),(3,4),(6,7)」のようなグループ分けだった場合は、

グループ内の人数 グループの個数
0人 0グループ
1人 3グループ
2人 2グループ
3人 0グループ

のようになっている。
A,Bは表左の人数に関する条件、CDは表右のグループ数に関する条件を表しており、条件を満たすならこのグループ分けは1カウントに数え上げられる。


数え上げなのでdpを考えてみると、「グループ内の人数」と「それまでの確定した人数」を状態にした以下のようなdpが考えられる。

dp[i][j] := i人以下のグループのみでグループを作ったとして、その合計人数がj人であるようなグループ分けの通り数

漸化式を考えると、i-1までが求められていたとして、i人グループを作ることを考えると、グループをx個作る場合は、

dp[i][j] = Σ_x f(i,j,x) * dp[i-1][j-i*x]

ここでf(i,j,x)は「残っている人で、i人グループをx個作る場合のグループ分けの数」。


これはiとjとxでO(N^3)っぽく見えるが、xはグループの個数で、iが大きくなるほど作れるグループ数は少なくなり、xはj/i個までしか作れないので、これはO(N^2 logN)となる。
( O(N + N/2 + N/3 + ...) = O(N logN) )
したがって、f()が十分に高速に求められれば間に合う。


f(i,j,k)を求める。
N人中すでに(j-i*x)人はグループになっているので、残りの(N-j+i*x)人からi人グループをx個作ることになる。
各人は番号がついていて区別できるので、並びも考慮するイメージで、「(N-j+i*x)人からi*x人を並べる通り数 (N-j+i*x)_P_(i*x)」から、重複分の「グループ内での並びは関係ない分 ( i! )^x」と「グループ自体の並びも関係ない分 x!」を割ってあげた以下の式になる。

f(i,j,k) = (N-j+i*x)_P_(i*x) / { ( i! )^x * x! } = (N-j+i*x) ! / { (N-j)! * (i!)^k * x! }

MOD階乗、MOD累乗、MOD逆元でこれを求めてあげればよい。

その他

ベル数 - Wikipedia

A,B,C,Dの条件がない場合は「ベル数」と呼ばれる。