询问
[a,b]
[
a
,
b
]
拆成
[1,a−1]
[
1
,
a
−
1
]
和
[1,b]
[
1
,
b
]
。
状态定义:
f[i][j][k][h][0/1]
f
[
i
]
[
j
]
[
k
]
[
h
]
[
0
/
1
]
表示:
到了第
i
i
位,各位数的和为,整个数模
k
k
为,第五维为
0
0
表示不卡或
b
b
的上界,否则卡上界。
然后枚举第位数的数值,大力转移即可。
这样空间是爆掉的,但可以发现一个性质:
转移的过程中,
k
k
的值是不变的。
因此可以先枚举,每次做一遍DP,
f[i][j][h][0/1]
f
[
i
]
[
j
]
[
h
]
[
0
/
1
]
同上。
可使用记忆化搜索,去掉不可能的状态,提高时间效率。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
typedef long long ll;
const int N = 27, M = 178;
int n, a[N], times, mark[N][M][M][2], pw[M][M];
ll f[N][M][M][2];
ll dp(int x, int sm, int md, int op, int MX)
{
if (sm < 0) return 0;
if (x == 1)
{
mark[x][sm][md][op] = times;
if (sm > 9) return f[x][sm][md][op] = 0;
if (sm % MX != md) return f[x][sm][md][op] = 0;
if ((!op && sm > a[1]) || (op && sm <= a[1]))
return f[x][sm][md][op] = 0;
return f[x][sm][md][op] = 1;
}
if (mark[x][sm][md][op] == times)
return f[x][sm][md][op];
int i; ll ans = 0;
For (i, (op ? a[x] : 0), (op ? 9 : a[x]))
if (i != a[x]) ans += dp(x - 1, sm - i,
(md - pw[MX][x - 1] * i % MX + MX) % MX, 0, MX) +
dp(x - 1, sm - i,
(md - pw[MX][x - 1] * i % MX + MX) % MX, 1, MX);
else ans += dp(x - 1, sm - i,
(md - pw[MX][x - 1] * i % MX + MX) % MX, op, MX);
return mark[x][sm][md][op] = times, f[x][sm][md][op] = ans;
}
ll solve(ll x)
{
int i;
n = 0;
if (x == 0) return 0;
while (x) a[++n] = x % 10, x /= 10;
ll ans = 0;
For (i, 1, 162)
times++, ans += dp(n, i, 0, 0, i);
return ans;
}
int main()
{
int MX, j;
For (MX, 1, 162)
{
pw[MX][0] = 1 % MX;
For (j, 1, 162)
pw[MX][j] = pw[MX][j - 1] * 10 % MX;
}
ll A, B;
cin >> A >> B;
cout << solve(B) - solve(A - 1) << endl;
return 0;
}