Div.1 B & Div.2 D : Numbers

問題概要

各数字を特定回数以上使って構成できる、n 桁以下の数はいくつあるか。mod 10^9+7 で示せ。

考え方

まず "n 桁以下の数" という条件が面倒なので、一旦 "k 桁の数" と考えなおしてみる。結局はこの k を 1\leq k\leq n でループして計算すればいいだけの話なので。
とりあえず 0 は最上位に入らないので {}_{k-1}C_{b_0} パターン(a_0\leq b_0\leq k)の組み合わせがある。そして 1 以降も r 桁分残っている場合、[tex:{}_{n-r}C_{b_j} パターンの組み合わせがある。なのでこの関係を DP で計算することになる。今回のプログラムでは [数字 d][既に埋めている桁数 k][数字 d を使っている個数 j] という風に組んでいる。

コード

#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int64;
const int64 MOD =1000000007ll;
int comb[101][101];
int64 Comb(int n, int k) {
  if (comb[n][k]) return comb[n][k];
  if (n == k || k == 0) return comb[n][k] = 1;
  return comb[n][k] = (Comb(n - 1, k) + Comb(n - 1, k - 1)) % MOD;
}
int64 dp[10][101][101];
int Solve(const int n, const int *a) {
  int64 m = 0;
  for (int i = 0; i < 10; ++i) m = m + a[i];

  int64 sum = 0;
  for (; m <= n; ++m) {
    memset(dp, 0, sizeof(dp));
    // 0 だけ特別処理
    for (int i = a[0]; i <= m - 1; ++i) dp[0][i][i] = Comb(m - 1, i);
    for (int d = 1; d < 10; ++d) for (int k = 0; k <= m; ++k) for (int j = 0; j <= k; ++j) for (int i = a[d]; i <= m - k; ++i) {
            int64 add = dp[d - 1][k][j] * Comb(m - k, i) % MOD;
            dp[d][k + i][i] = (dp[d][k + i][i] + add) % MOD;
          }
    for (int i = a[9]; i <= m; ++i) sum = (sum + dp[9][m][i]) % MOD;
  }
  return sum;
}
int main(void) {
  int a[10], n;
  cin >> n;
  for (int i = 0; i < 10; ++i) cin >> a[i];
  cout << Solve(n, a) << "\n";
  return 0;
}