と小さいので、すべての に対しての答えを求めます。
の倍数の条件は の倍数かつ の倍数であることです。また、それぞれの倍数判定法は以下であることが知られています。
以下のような方針で2つの情報をもちながらDPをすることが出来ます。
桁を決定したときの桁和が 、末尾の偶奇が ( or ) の数
とします。
遷移は を選ぶ 通りあります。(の時、 を選ぶ遷移はないことに注意してください)。
遷移は以下のようになります。
(ここで )
桁の場合の答えは の が の倍数であり、 であるもののすべての の和です。 計算量は です。
mod = 10**9 + 7 m = 305 dp = [[[0 for k in range(2)]for j in range(m * 10 + 10)]for i in range(m + 1)] A = [0] * m dp[0][0][0] = 1 for i in range(m): for j in range(m * 10 + 10): for k in range(2): if(dp[i][j][k] != 0): for l in range(10): if(i == 0 and l == 0): continue ni = i + 1 nj = j + l dp[ni][nj][l%2] += dp[i][j][k] a = 0 for j in range(m*10+10): if(j % 3 == 0): a += j * dp[i][j][0] a %= mod A[i] = a t = int(input()) for _ in range(t): n = int(input()) print(A[n])
の倍数の末尾の数字は のいずれかであるため、末尾の桁から数字を決定していくと、二次元DPにすることができます。 の時に注意してください。
#include<bits/stdc++.h> using namespace std; using ll = long long; ll mod = 1'000'000'007; int main() { int maxN = 300; vector<vector<ll>> dp(maxN + 1, vector<ll>(maxN * 9, 0)); vector<ll> ans(maxN + 1, 0); for (int i = 0; i <= 8; i += 2) dp[1][i] = 1; ans[1] = 6; for (int i = 2; i <= maxN; i++) { for (int j = 0; j < maxN * 9; j++ ) { for (int k = 1; k < 10; k++){ if (j - k >= 0) dp[i][j] += dp[i - 1][j - k]; } if (j % 3 == 0) ans[i] += dp[i][j] * j % mod; dp[i][j] += dp[i - 1][j]; dp[i][j] %= mod; } ans[i] %= mod; } int q; cin >> q; for (int i = 0; i < q; i++) { int n; cin >> n; cout << ans[n] << endl; } }