読者です 読者をやめる 読者になる 読者になる

4bitにっき

ぽよ

AtCoder Regular Contest 052 D - 9

解法

愚直解では太刀打ち出来ないが、各桁の総和はせいぜい100もないので、Kで割った余りが0~100くらいまでの数だけを調べるように改善すれば、 O((Mの桁数)\frac{M}{K})アルゴリズムになる。
これはKが大きい時に有効そうなので、Kが小さい時を他のアルゴリズムで倒せないか考える。
それは下のような桁DPで解決できる。
dp[i][j][k][b] = 「{i桁目までが決まった}, {Kで筆算的に計算してきた余りがj}, {各桁の和%Kがj}, {bが0の場合は決めてきた桁がMを超え得る状態であり, 1の場合はそうでない}」という状態の総数。
(日本語が下手)
これは各桁の総和が最大でもSとすると、O((Mの桁数)SNK)

実装

半分全列挙解があるらしい。頭良さそう

#include <bits/stdc++.h>
 
using namespace std;
 
#define int long long
 
int K, M;
string strM;
int memo[10][1001][1001][2];
 
int DFS(int n, int rem0, int rem1, bool flg)
{
    if (n == strM.size()) return (rem0 == rem1);
    int &_memo = memo[n][rem0][rem1][flg];
    if (_memo != -1) return _memo;
    int res = 0;
    for (int i = 0; i < 10; i++)
    {
        if (flg && strM[n] - '0' < i) break;
        bool nflg = false;
        if (flg && strM[n] - '0' == i) nflg = true;
        res += DFS(n + 1, (rem0*10 + i) % K, (rem1 + i) % K,  nflg);
    }
    return _memo = res;
}
 
int SolveSmall()
{
    //K^2*(Mの桁数)
    fill((int*)memo, (int*)memo + 10*1001*1001*2, -1);
    return DFS(0, 0, 0, true) - 1;
}
 
bool IsOK(int n)
{
    int w = 1, sum = 0;
    for (int i = 0; i < 11; i++, w *= 10)
    {
        sum += (n/w) % 10;
    }
    return (n % K == sum % K);
}
 
int SolveLarge()
{
    //M/K*(Mの桁数)*9
    int ans = 0;
    for (int i = 0; i <= M; i += K)
    {
        for (int j = 0; j < min(90ll, K); j++)
        {
            ans += IsOK(i + j);
        }
    }
    return ans - 1;
}
 
signed main()
{
    cin >> K >> strM;
    stringstream ss(strM);
    ss >> M;
    if (K < 1000) printf("%lld\n", SolveSmall());
    else printf("%lld\n", SolveLarge());
    return 0;
}