題目鏈接:https://atcoder.jp/contests/abc336/tasks/abc336_e
題目大意:
我們定義一個整數 \(n\) 的 數位和 爲 \(n\) 的十進制表示中的各位上的數字之和。比如:整數 \(2024\) 的數位和爲 \(2 + 0 + 2 + 4 = 8\)。
一個正整數 \(n\) 被稱作一個 好數 如果 \(n\) 能被它的數位和整除。舉例,\(2024\) 是好數,因爲它能被它的數位和 \(8\) 整除。
給你一個整數 \(N(1 \le N \le 10^{14})\),請你計算有多少 \(\le N\) 的好數。
輸入 \(N\),輸出 \(\le N\) 的好數個數。
解題思路:
枚舉數位和 \(X\)(\(X\) 最大也就 \(14 \times 9\)),然後對於每一個 \(X\),做一遍數位DP。
定義狀態 \(f_{p, x, y}\) 表示當前在第 \(p\) 位,到第 \(p\) 位爲止的數模 \(X\) 爲 \(x\),到第 \(p\) 位爲止的數的數位和爲 \(y\) 對應的數字個數。
則最終(邊界條件)只有當 \(p = -1\) 時,只有 \(x = 0\) 且 \(y = X\) 的狀態對應的個數爲 \(1\)。
示例程序:
#include <bits/stdc++.h>
using namespace std;
int a[22], X;
// f[p][x][y] 第 p 爲,模 X = x,數位和 y
long long N, ans, f[22][140][140][2];
bool vis[22][140][140][2];
long long dfs(int p, int x, int y, bool limit) {
if (p < 0) {
return x == 0 && y == X;
}
if (vis[p][x][y][limit]) return f[p][x][y][limit];
vis[p][x][y][limit] = true;
long long &res = f[p][x][y][limit];
res = 0;
int up = limit ? a[p] : 9;
for (int i = 0; i <= up; i++) {
int xx = (x * 10 + i) % X, yy = y + i;
res += dfs(p-1, xx, yy, limit && i == up);
}
return res;
}
long long cal(long long num) {
memset(vis, 0, sizeof vis);
int p = 0;
while (num) {
a[p++] = num % 10;
num /= 10;
}
return dfs(p-1, 0, 0, 1);
}
int main() {
scanf("%lld", &N);
for (X = 1; X <= 14 * 9; X++)
ans += cal(N);
printf("%lld\n", ans);
return 0;
}