这题可以包含前导0,因为前导0对各位数字之和没有影响。
先预处理出f[i, j, k]
,代表位数是i
,最高位是j
,被N
整除余k
的数的个数。
例如f[3, 6, 9]
就是600~699中被N整数余9的数的个数。
对于f[i, j, k]
,加上i-1位,最高位是l的余数符合要求的数f[i-1, l, t]
即可,其中
(
t
+
j
)
%
n
=
k
(t+j)\%n=k
(t+j)%n=k。
换而言之
t
=
k
+
p
∗
n
−
j
t=k+p*n-j
t=k+p∗n−j,
p
=
0
,
1
,
2
,
.
.
.
p=0,1,2,...
p=0,1,2,...
之后同其他数位DP分析方式相同,先看最高位
a
n
−
1
a_{n-1}
an−1,枚举最高位
j
j
j是
0
a
n
−
1
−
1
0~a_{n-1}-1
0 an−1−1的情况,将满足的数的个数加入答案,注意这边有
i
+
1
i + 1
i+1位,答案要加
f
[
i
+
1
]
[
j
]
[
t
]
f[i + 1][j][t]
f[i+1][j][t],接着更新
l
a
s
t
last
last,看下一位。
l
a
s
t
last
last用来记录前面几位的和,因此这边的
t
t
t满足
(
t
+
l
a
s
t
)
%
n
=
0
(t+last)\%n=0
(t+last)%n=0。换而言之
t
=
p
∗
n
−
l
a
s
t
t=p*n-last
t=p∗n−last,
p
=
0
,
1
,
2
,
.
.
.
p=0,1,2,...
p=0,1,2,...
这题不看到最后一位尚不知道所有答案,因此不会提前break退出。
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 105;
const int M = 15; // 2^31 < 1e10
int a, b, n;
int f[M][M][N];
void init(int n) {
for (int j = 0; j <= 9; j ++ ) {
f[1][j][j % n] ++ ;
}
for (int i = 2; i < M; i ++ ) {
for (int j = 0; j <= 9; j ++ ) {
for (int k = 0; k < n; k ++ ) {
for (int l = 0; l <= 9; l ++ ) {
for (int p = 0; k + p * n - j < n; p ++ ) {
int t = k + p * n - j;
if (t >= 0) f[i][j][k] += f[i - 1][l][t];
}
}
}
}
}
}
int dp(int num) {
if (!num) return 1;
vector<int> nums;
while(num) nums.push_back(num % 10), num /= 10;
int res = 0, last = 0;
for (int i = nums.size() - 1; i >= 0; i -- ) {
int x = nums[i];
for (int j = 0; j < x; j ++ ) {
for (int k = 0; k * n - last < n; k ++ ) {
int t = k * n - last;
if (t >= 0) res += f[i + 1][j][t];
}
}
last = (last + x) % n;
if (!i && !last) res ++ ;
}
return res;
}
int main() {
while(~scanf("%d%d%d", &a, &b, &n)) {
memset(f, 0, sizeof f);
init(n);
printf("%d\n", dp(b) - dp(a - 1));
}
return 0;
}
由于上面初始化的 t t t满足 ( t + j ) % n = k (t+j)\%n=k (t+j)%n=k,dp过程中的 t t t满足 ( t + l a s t ) % n = 0 (t+last)\%n=0 (t+last)%n=0。由 f f f的定义,这边的 t t t也是 % n \%n %n的余数,范围在 [ 0 , n − 1 ] [0,n-1] [0,n−1],因此不必枚举 p p p,只需自定义取模的规则mod,使得mod结果都为正数即可。
int mod(int x, int y) {
return (x % y + y) % y; // 所有模数返回正数
}
这样 ( t + j ) % n = k (t+j)\%n=k (t+j)%n=k变成 t = m o d ( k − j , n ) t=mod(k-j, n) t=mod(k−j,n), ( t + l a s t ) % n = 0 (t+last)\%n=0 (t+last)%n=0变成 t = m o d ( − l a s t , n ) t=mod(-last, n) t=mod(−last,n)
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 105;
const int M = 15; // 2^31 < 1e10
int a, b, n;
int f[M][M][N];
int mod(int x, int y) {
return (x % y + y) % y; // 所有模数返回正数
}
void init(int n) {
for (int j = 0; j <= 9; j ++ ) {
f[1][j][j % n] ++ ;
}
for (int i = 2; i < M; i ++ ) {
for (int j = 0; j <= 9; j ++ ) {
for (int k = 0; k < n; k ++ ) {
for (int l = 0; l <= 9; l ++ ) {
f[i][j][k] += f[i - 1][l][mod(k - j, n)];
}
}
}
}
}
int dp(int num) {
if (!num) return 1;
vector<int> nums;
while(num) nums.push_back(num % 10), num /= 10;
int res = 0, last = 0;
for (int i = nums.size() - 1; i >= 0; i -- ) {
int x = nums[i];
for (int j = 0; j < x; j ++ ) {
res += f[i + 1][j][mod(-last, n)];
}
last = (last + x) % n;
if (!i && !last) res ++ ;
}
return res;
}
int main() {
while(~scanf("%d%d%d", &a, &b, &n)) {
memset(f, 0, sizeof f);
init(n);
printf("%d\n", dp(b) - dp(a - 1));
}
return 0;
}