TDPC E 数

人と関わって成し遂げる事は難しいので、少なくとも一人でできることはちゃんとやろう。えぬわいです。

qiita.com

続きです。

atcoder.jp

DP週間です。

考えたこと

再帰関数で、和を足していけばよい。と考えていました。結果的に、全ての数を捻出する方法がわからなかったので、詰みました。答えというか桁DPについて、調べました。

桁DPとは

drken1215.hatenablog.com

さすが、けんちょんさんだぜ👏 そこにシビれるあこがれウゥ。

けんちょんさんのブログがやはりわかりやすいです。

自分にとってのこの問題の肝は、再帰関数で、いかに全ての数字を羅列するかでした。

そこで、参照ブログを参考に、数字を羅列する桁DP前の再帰関数を書いてみました。

n = input()
ans = []

def rec(pos, s, a):
    if pos == len(n):
        return ans.append("".join(a))
    if s:
        for d in range(0, 9+1):
            a.append(str(d))
            rec(pos+1, True, a)
            a.pop()
    else:
        # K と一致する場合
        a.append(n[pos])
        rec(pos+1, False, a)
        a.pop()
        for d in range(0, int(n[pos])):
            a.append(str(d))
            rec(pos+1, True, a)
            a.pop()

# 左の桁から 0 桁目は自由ではない
rec(0, False, [])
print(sorted(ans))

こんな感じで、a を呼び出した引数の記録媒体としています。 実行すると

% python keta_dp.py                                                                   
100
['000', '001', '002', '003', '004', '005', '006', '007', '008', '009', '010', '011', '012', '013', '014', '015', '016', '017', '018', '019', '020', '021', '022', '023', '024', '025', '026', '027', '028', '029', '030', '031', '032', '033', '034', '035', '036', '037', '038', '039', '040', '041', '042', '043', '044', '045', '046', '047', '048', '049', '050', '051', '052', '053', '054', '055', '056', '057', '058', '059', '060', '061', '062', '063', '064', '065', '066', '067', '068', '069', '070', '071', '072', '073', '074', '075', '076', '077', '078', '079', '080', '081', '082', '083', '084', '085', '086', '087', '088', '089', '090', '091', '092', '093', '094', '095', '096', '097', '098', '099', '100']

というように、全て羅列できることがわかります。ただし、0 も含まれているので、注意しなければなりません。

これをベースに、問題を解いていきます。

D = int(input())
n = input()
MOD = 1e9+7

"""
def rec(pos, s, a):
    if pos == len(n):
        return 1 if a == 0 else 0
    ans = 0
    if s:
        for d in range(0, 9+1):
            ans += rec(pos+1, True, (a+d)%D)
    else:
        # K と一致する場合
        ans += rec(pos+1, False, (a+int(n[pos]))%D)
        for d in range(0, int(n[pos])):
            ans += rec(pos+1, True, (a+d)%D)
    return ans

# 左の桁から 0 桁目は自由ではない
print(rec(0, False, 0)-1)
"""

dp = [[[0 for k in range(D)] for j in range(2)] for i in range(len(n)+1)]
dp[len(n)][0][0] = 1
dp[len(n)][1][0] = 1

for i in range(len(n)-1, -1, -1):
    for j in range(2):
        for k in range(D):
            if j == 1:
                for d in range(0, 9+1):
                    dp[i][j][k] += dp[i+1][1][(k+d)%D]
                    dp[i][j][k] %= MOD
            else:
                dp[i][j][k] += dp[i+1][0][(k+int(n[i]))%D]
                for d in range(0, int(n[i])):
                    dp[i][j][k] += dp[i+1][1][(k+d)%D]
                    dp[i][j][k] %= MOD
print(int(dp[0][0][0]-1))

TLEになりました

C++ で書き直しました。

#include <cstdio>
#include <iostream>
using namespace std;

long long int dp[10001][2][100];
long long int MOD = 1e9+7;

int main() {
    string N;
    int D;
    scanf("%d", &D);
    cin >> N;
    dp[N.size()][1][0] = 1;
    for (int i = N.size()-1; i >= 0; i--) {
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < D; k++) {
                if (j == 1) {
                    for (int d = 0; d < 10; d++) {
                        (dp[i][j][k] += dp[i+1][1][(k+d)%D]) %= MOD;
                    }
                } else {
                    dp[i][j][k] += dp[i+1][0][(k+N[i]-'0')%D];
                    for (int d = 0; d < N[i]-'0'; d++) {
                        (dp[i][j][k] += dp[i+1][1][(k+d)%D]) %= MOD;
                    }
                }
            }
        }
    }
    printf("%lld\n", dp[0][0][0]-1);
}

ACしました

いよいよ python で競プロはできないかもしれないと思い始めました。