要求:一个可被自身每一位的数整除的数叫做美丽数,输出[a,b]内美丽数的个数。
方法:数位dp 记忆化搜索 lcm(最小公倍数)
1.dp[i][j][k]表示i位数、模2520为j、每位上数的最小公倍数为k 的个数。1~9相乘得2520。
2.一个数可以整除自身每一位上的数,等价于可以整除自身每一位上的数的lcm。因此记录每位数的lcm即可。可将此位添加后模2520并传递到下一层,因为传递模2520与传递原数在最后模lcm的数一样。
3.套路记忆话搜索即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
long long dp[25][2600][50] ;
//dp[i][j][k]表示i位数 模2520为j 每位上数的最小公倍数为k 的个数
int digit[25] ;
int index[2600] ;
void init()
{
int i ;
int num = 0 ;
for(i = 1 ; i <= 2520 ; i ++)
if(2520 % i == 0)
index[i] = num ++ ;
}
int gcd(int a , int b)
{
if(a == 0)
return b ;
return gcd(b % a , a);
}
int LCM(int a , int b)
{
return a / gcd(a,b) * b;
}
long long dfs(int len , int mod , int lcm , int lim)
{
int i , j ;
int mod1 ;
int lcm1 ;
int num ;
long long ans = 0 ;
if(len <= 0)
return mod % lcm == 0 ;
if(!lim && dp[len][mod][index[lcm]] != -1)
return dp[len][mod][index[lcm]] ;
if(lim)
num = digit[len] ;
else
num = 9 ;
for(i = 0 ; i <= num ; i ++)
{
mod1 = (mod * 10 + i) % 2520 ;
if(i > 0)
lcm1 = LCM(lcm , i) ;
else
lcm1 = lcm ;
ans += dfs(len - 1 , mod1 , lcm1 , lim && i == num) ;
}
if(!lim)
dp[len][mod][index[lcm]] = ans ;
return ans ;
}
long long solve(long long x)
{
int len ;
len = 0 ;
while(x)
{
digit[++len] = x % 10 ;
x /= 10 ;
}
return dfs(len , 0 , 1 , 1) ;
}
int main()
{
int t ;
int i , j , k ;
long long a , b ;
long long ans ;
int len ;
init() ;
memset(dp , -1 , sizeof(dp)) ;
scanf("%d" , &t) ;
while(t --)
{
cin >> a >> b ;
ans = solve(b) - solve(a - 1) ;
cout << ans << endl ;
}
}