思路:
数位DP,维护三个量:
pos:当前处理到的位数
sta :状态压缩,保存该数数位上的数出现情况,只需要保存 2 ~ 9
mo : 之前处理过的数位上的数的最小公倍数。(因2…9的最小公倍数为2520,故可取模节省内存)
w[]:预处理每一位为1时对2520取模的余数。
然后记忆化搜索即可。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 2520;
ll dp[20][260][2600];
int dig[20],w[20];
int tot;
ll dfs(ll pos,ll sta,ll mo,ll limit){
if(!limit && dp[pos][sta][mo] != -1) return dp[pos][sta][mo];
if(pos < 1){
bool flag = true;
for(int i=2 ;i<10 ;i++){
if(sta&(1<<(i-2))){
if(mo % i){
flag = false;
break;
}
}
}
if(flag) return 1;
return 0;
}
int end = limit?dig[pos]:9;
ll res = 0;
for(int i=0 ;i<=end ;i++){
ll _sta = sta;
if(i >= 2) _sta |= (1<<(i-2));
res += dfs(pos-1,_sta,(mo + i*w[pos])%mod,limit&&i==end);
}
if(!limit) dp[pos][sta][mo] = res;
return res;
}
ll solve(ll x){
tot = 0;
while(x){
dig[++tot] = x%10;
x /= 10;
}
return dfs(tot,0,0,1);
}
int main(){
memset(dp,-1,sizeof(dp));
w[1] = 1;
for(int i=2 ;i<20 ;i++){
w[i] = (w[i-1] * 10) % mod;
}
int T;
scanf("%d",&T);
while(T--){
ll l,r;
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",solve(r) - solve(l-1));
}
return 0;
}