思路
首先我们要知道,一个数字num能够被所有的位数(除了0)整除,说明num是它所有的位数的公倍数。记所有数位的最小公倍数为LCM,则有 n u m % L C M = = 0 num \% LCM == 0 num%LCM==0
容易知道,1、2、3……8、9的最小公倍数为2520。
2520的因数一共有48个,题目给的内存不够我们开dp[25][2530][2530],所以我们把最后一个维度(2520的因数)离散化编号。
然后进行数位dp即可
代码
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long ll;
const int MOD = 2520;
int Hash[2530];
int digit[25];
ll dp[25][2530][50];
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
int lcm(int a, int b){
return a * b / gcd(a, b);
}
ll dfs(int pos, int mod, int Lcm, bool limit){
if(pos == 0)
return mod % Lcm == 0;
if(!limit && dp[pos][mod][Hash[Lcm]] != -1)
return dp[pos][mod][Hash[Lcm]];
ll ans = 0;
int up = limit ? digit[pos] : 9;
for(int i = 0; i <= up; i++)
{
if(i == 0){
ans += dfs(pos - 1, (mod * 10 + i) % MOD, Lcm, limit && i == up);
}else{
ans += dfs(pos - 1, (mod * 10 + i) % MOD, lcm(Lcm, i), limit && i == up);
}
}
if(!limit)
dp[pos][mod][Hash[Lcm]] = ans;
return ans;
}
ll bit(ll num){
int len = 0;
while(num){
digit[++len] = num % 10;
num/= 10;
}
return dfs(len, 0, 1, 1);
}
void init(){
int cnt = 0;
int num = 2520;
for(int i = 1; i <= num; i++)
if(!(num % i))
Hash[i] = ++cnt;
}
int main(){
init();//哈希
memset(dp, -1, sizeof(dp));
int t;
scanf("%d", &t);
while(t--){
ll l, r;
scanf("%I64d %I64d", &l, &r);
printf("%I64d\n", bit(r) - bit(l - 1));
}
return 0;
}
参考来源
博客
https://www.cnblogs.com/dilthey/p/8542011.html
博客
https://www.cnblogs.com/neopenx/p/4063647.html