//数位dp,1到9的最小公倍数是2520。dfs里记录当前位置pos,已经得到的数模2520的值before,已经得到的数位的最小公倍数的标号lcm(直接设为最小公倍数时dp数组会开的很大,所以可以映射一下减少空间复杂度),之前位是否达到上限的标志位flag。然后直接记忆化搜索,pos=-1时判断一下就好了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
ll dp[20][2521][50];
int num[20],k;
int to[2521];
int from[50];
void init()
{
int t=sqrt(2520),cnt=0;
for(int i=1;i<t;i++)
{
if(2520%i==0)
{
to[i]=cnt,from[cnt++]=i;
to[2520/i]=cnt,from[cnt++]=2520/i;
}
}
memset(dp,-1,sizeof(dp));
}
int gcd(int x,int y)
{
int tt;
while(x%y)
{
tt=x%y;
x=y;
y=tt;
}
return y;
}
int LCM(int x,int y)
{
return x/gcd(x,y)*y;
}
ll dfs(int pos,int before,int lcm,bool flag)
{
if(pos<0)
{
if(before%from[lcm]==0)return 1;
return 0;
}
if(!flag&&dp[pos][before][lcm]!=-1)return dp[pos][before][lcm];
ll res=0;int i;
int ma=flag?num[pos]:9;
for(i=0;i<=ma;i++)
{
int temp=lcm;
if(i)temp=to[LCM(from[lcm],i)];
res+=dfs(pos-1,(before*10+i)%2520,temp,flag&&i==ma);
}
if(!flag)dp[pos][before][lcm]=res;
return res;
}
int main()
{
int t;ll l,r;
init();
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&l,&r);
k=0,l--;
memset(num,0,sizeof(num));
while(l)
{
num[k++]=l%10;
l/=10;
}
l=dfs(k-1,0,0,1);
k=0;
memset(num,0,sizeof(num));
while(r)
{
num[k++]=r%10;
r/=10;
}
r=dfs(k-1,0,0,1);
printf("%I64d\n",r-l);
}
}