美丽数就是该数可以被他的每一位数字整除。
dp[i][j][k]表示处理到数位i,该数对2520取模为j,各个数位的LCM为k
分析:一个数能被它的所有非零数位整除,则能被它们的最小公倍数整除,而1到9的最小公倍数为2520,
数位DP时我们只需保存前面那些位的最小公倍数就可进行状态转移,到边界时就把所有位的lcm求出了,
为了判断这个数能否被它的所有数位整除,我们还需要这个数的值,显然要记录值是不可能的,其实我们只
需记录它对2520的模即可,这样我们就可以设计出如下数位DP:dfs(pos,mod,lcm,f),pos为当前
位,mod为前面那些位对2520的模,lcm为前面那些数位的最小公倍数,f标记前面那些位是否达到上限,
这样一来dp数组就要开到19*2520*2520,明显超内存了,考虑到最小公倍数是离散的,1-2520中可能
是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 ll;
const int mod = 2520;
ll dp[25][2520][48];
int index[2600], bit[25];
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a%b);
}
int lcm(int a, int b)
{
return a*b / gcd(a, b);
}
void init()
{
int num = 0;
for (int i = 1;i <= mod;i++)
{
if (mod%i == 0)
index[i] = num++;
}
}
ll dfs(int pos, int presum, int prelcm, bool flag)
{
if (pos == -1) return presum%prelcm == 0;
if (!flag&&dp[pos][presum][index[prelcm] ]!=-1)
return dp[pos][presum][index[prelcm]];
ll ans = 0;
int end = flag ? bit[pos] : 9;
for (int i = 0;i <= end;i++)
{
int nowsum = (presum * 10 + i) % mod;
int nowlcm = prelcm;
if (i) nowlcm = lcm(prelcm, i);
ans += dfs(pos - 1, nowsum, nowlcm, flag&&i == end);
}
if (!flag) dp[pos][presum][index[prelcm]] = ans;
return ans;
}
ll calc(ll x)
{
int pos = 0;
while (x)
{
bit[pos++] = x % 10;
x = x / 10;
}
return dfs(pos - 1, 0, 1, 1);
}
int main()
{
ll t, l, r;
init();
memset(dp, -1, sizeof(dp));
scanf("%I64d", &t);
while (t--)
{
scanf("%I64d%I64d", &l, &r);
printf("%I64d\n", calc(r) - calc(l - 1));
}
return 0;
}