予鈴

アウトプットとメモ書きの中間みたいな記事がたくさん出ます。

D - Digits Parade

問題

atcoder.jp
以下では、最も大きな桁を1番目として考えてます。あとこの記事はかなり備忘録寄りになってます

考察

・ひと目みて桁DPだと気づいたよ。すっかり忘れていたのでこのサイトで復習した。
・余りが5である必要があるので、各桁について余りを管理する必要がある。大きい桁から計算するメモ化再帰を考えた。
・メモ配列  memo[ digit ] [ num ] [ mod]digit目の数字をnumにしたとき、13で割った余りが modである場合の数と定義する。このとき、 nmod \,を  nmod + \, 桁の重みの余り \, * \, num \, = mod となる数とすると、更新式は以下の通り。

 memo[ digit ] [ num ] [ mod] +=  \,memo[ digit + 1 ] [ 次の桁の数字  ] [ nmod]

・イメージは、digit桁目にnumを使って余りがmodになるためには、次の桁以降の余りが nmod になっていなければならない。

digit + 1が?ならば0~9を試せば良い。桁の余りの重みも簡単に求めることができる。 10^u = 9 * 10^{u-1} + 10^{u-1}なので、順番に余りを求めていけば良い。コード中のbaseがそれに該当する
提出したコードはこれ
atcoder.jp

・実際は2次元でできる。必要なのは桁とmodだけの情報で良い。上記の提出でもnumはnmodを求める場合のみ用いている。
・上記のコードは関数呼び出し時に桁の数字を決める、下のコードは関数内でいま見ている桁をどうするか決める。
・感覚的にはdigit目の文字が'?'であれば、より多くのnmodを取る事ができる。例えば、digit桁目がn,uでもcmodの値がおなじ場合があるので。

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define int ll
template<typename Head, typename Value> auto vectors(const Head &head, const Value &v) { return vector<Value>(head, v); }
template<typename Head, typename... Tail> auto vectors(Head x, Tail... tail) { auto inner = vectors(tail...); return vector<decltype(inner)>(x, inner); }
constexpr int MOD = 1000000007;

signed main(){
    string s; cin >> s;
    int N = s.size();
    auto memo = vectors( N + 2,13,-1LL);
    // 0-index
    vector<int>base(s.size() + 1,0LL);
    base[ N - 1] = 1;
    
    for(int i = N - 2; i >= 0; --i)
        base[i] = (9 * base[i + 1] + base[i + 1]) % 13;
    
    for(int i = 0; i < 13; ++i)
        memo[s.size() - 1][i] = (i <= 9 and (s.back() == '?' or s.back() - '0' == i) ? 1 : 0);
    
    
    function<int(int,int)>solver = [&](int digit,int cmod){
        if( memo[digit][cmod] != -1LL) return memo[digit][cmod];
        memo[digit][cmod] = 0;
        // digit桁目でcmodとなる場合の数を求める。 関数内で次の数字を決める。
        int idx = digit;
        //今見ている桁が'?'のとき、0~9をすべて試す
        if(s[idx] == '?'){
            for(int num = 0; num < 10; ++num){
                int temp =  ( base[digit] * num ) % 13;
                int nmod =  (cmod - temp <= 0 ? cmod - temp + 13 : cmod - temp) % 13;
                memo[digit][cmod] += solver(digit + 1,nmod);
                memo[digit][cmod] %= MOD;
            }
        } else {
            int num = s[idx] - '0';
            int temp =  ( base[digit] * num ) % 13;
            int nmod =  (cmod - temp <= 0 ? cmod - temp + 13 : cmod - temp) % 13;
            memo[digit][cmod] += solver(digit + 1, nmod);
            memo[digit][cmod] %= MOD;
        }
        return (memo[digit][cmod]) % MOD;
    };
    cout << (solver(0,5)) << endl;
}