题意:给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。
分析:一个数的数字和是很小的,最大为9*18,那么我们可以枚举这个数位和,因为这个数位和是要作模数的,只能枚举,所以dp的时候必须要加上这一维度才好递推。f[i][j][k][0/1]表示到第i位数位和为j,在模sum意义下的余数为k,是否卡上界的数的个数。f[0][i][j][k]表示前i位全都和原数x相等的数的个数(卡上界),f[1][i][j][k]表示前i位至少有一位小于x相应数位的数的个数(不卡上界)。
代码:
#include <cstring>
#include <iostream>
#define ll long long
using namespace std;
const int N = 200, L = 21;
ll f[L][N][N][2];
int n[L];
ll calc(ll x, int P) {
if (!x) return 0;
memset(f, 0, sizeof(f));
int t = 0;
while (x) {
n[++t] = x % 10;
x /= 10;
}
f[t+1][0][0][0] = 1;
for (int i = t + 1; i > 1; i--)
for (int j = 0; j <= P; j++)
for (int k = 0; k < P; k++)
if (f[i][j][k][0] || f[i][j][k][1])///剪枝
for (int p = 0; p < 10; p++) {
int w = (10 * k + p) % P;
if (p < n[i-1] && j + p <= P) ///计算不卡上界的贡献
f[i-1][j+p][w][1] += f[i][j][k][0];
else if (p == n[i-1] && j + p <= P) ///计算卡上界的贡献
f[i-1][j+p][w][0] += f[i][j][k][0];
if (f[i][j][k][1] && j + p <= P) ///如果不卡上界 计算p>n[i-1]情况下的贡献
f[i-1][j+p][w][1] += f[i][j][k][1];
}
return f[1][P][0][0] + f[1][P][0][1];
}
int main() {
ll a, b, ans = 0;
cin >> a >> b;
for (int i = 1; i <= 9 * 18; i++)///枚举数位和i
ans += calc(b, i) - calc(a - 1, i);
cout << ans << endl;
return 0;
}