题目链接:https://codeforces.com/problemset/problem/55/D
题意:找出区间【l,r】内一个数能被自已每位数整除的数的个数。
思路:数位dp,两个判定状态,一个是数的值x,一个是自己每个位数的最小公倍数lca。
数的值很大,可以优化数的值,x%lca=x%(n*lca)%lca=x%(lca(1 2 3....9))%lca=x%2520%lca;
1到9的lca一共只有48个
dp[i][j][k],i为位数,j为数的值,k为最小公倍数,表示当余位为两位,且数的值为j,最小公倍数为k时的答案
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
#define LL long long
#define mod 2520
using namespace std;
LL n,l,r;
LL d[20],dp[20][2520][50],dit[2520];
LL LCA(LL a,LL b)
{
return a/__gcd(a,b)*b;
}
LL dfs(LL len,LL sum,LL lca,LL flag)
{
if(len==0) return sum%lca==0;
if(!flag&&dp[len][sum][dit[lca]]!=-1) return dp[len][sum][dit[lca]];
LL up=flag?d[len]:9;
LL ans=0;
for(LL i=0;i<=up;i++)
{
ans+=dfs(len-1,(sum*10+i)%mod,i?LCA(lca,i):lca,flag&&i==up);
}
if(!flag) dp[len][sum][dit[lca]]=ans;
return ans;
}
LL get(LL x)
{
LL len=0;
while(x)
{
d[++len]=x%10;
x/=10;
}
return dfs(len,0,1,1);
}
int main()
{
LL cut=0;
for(LL i=1;i<=2520;i++)
{
if(2520%i==0) dit[i]=cut++;
}
memset(dp,-1,sizeof(dp));
scanf("%lld",&n);
while(n--)
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",get(r)-get(l-1));
}
return 0;
}