You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

59 lines
2.1 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
const int N = 170;
int a[20], mod;
/*
f[pos][sum][pre]
第一维数字的每一位从18~1有效
第二维:已经走完的路径,每位的数位和
第三维:已经走完的路径,每位的拼接值(十进制)
前提已经走到pos这个位置前面积累下来的数位和是sum,前面的数字拼接pre(10进制), (是不是贴上界)
返回:在满足前提条件下,后续符合题意的数字有多少个?
*/
ll f[20][N][N];
//此处注意一下pre如果是类型ll,则会第9个点TLE是类型int,则可以AC!
ll dfs(int pos, int sum, int pre, bool limit) {
// 如果走完了全程pre=原数%mod,如果最后pre还能整除mod,并且sum等于mod,则贡献数字1个
if (pos == 0) return pre == 0 && sum == mod;
if (!limit && ~f[pos][sum][pre]) return f[pos][sum][pre];
ll ans = 0;
int up = limit ? a[pos] : 9;
for (int i = 0; i <= up; i++) {
// sum+i:数位和+i
// (pre * 10 + i) % mod : 不断的拼接前缀数字(十进制)但不能盲目记录原始数字需要分组按什么分组呢当然是按枚举的当前mod分组
// 上面这句是本题的精华!
ans += dfs(pos - 1, sum + i, (pre * 10 + i) % mod, limit && i == a[pos]);
}
//记忆化结果
if (!limit) f[pos][sum][pre] = ans;
return ans;
}
ll calc(ll x) {
if (x == -1) return 0; //处理一下边界情况
int al = 0;
while (x) a[++al] = x % 10, x /= 10;
ll res = 0;
for (mod = 1; mod <= 9 * al; mod++) { // max(mod)=9*18=162,数量并不大,可以枚举
memset(f, -1, sizeof f); //每次取的模都会变化,这样状态数组就不再可以重用,需要每次清空
res += dfs(al, 0, 0, true); //其实本题的本质是按分组讨论的方式完成的数量的统计,然后再汇总总数量
}
return res; //返回结果
}
int main() {
ll l, r;
scanf("%lld%lld", &l, &r);
printf("%lld\n", calc(r) - calc(l - 1));
return 0;
}