题目链接:
题目大意:
求在[l,r]中能够整除自己每个数位上的数字的数的个数。
题目分析:
- 首先我们能够知道如果这个数能够整除它的每个数位上的数字,那么它一定能够整除他们的最小公倍数,是充要的。
- 那么我们定义状态dp[i][j][k]代表i位在任意组合下得到的所有数位的数字的最小公倍数为j的每个数位上的数字之积%2520为k的方案数。
- 我们可以知道所有的公倍数最大不会超过2520,而且他们都是2520的约数,所以如果他们能够整除2520的余数,那么证明他们能够整除所有数位上的数字之积,是充要的。
- 所以我们利用这样状态记录进行记忆化搜索,对于给定的数字求取比它小的所有的符合要求的数,间接的求出区间内合法的数的个数,具体求法就是对于给定数的每一位,枚举它可能的数字,如果小于当前位i的数字,那么后面方案数就是dp[n-i][j][k],找出满足条件的j和k即可,利用深搜来实现。如果等于当前位,那么固定当前位,更新lcm,然后转移到下一位去寻找方案数。
- 清楚了状态就是一个简单的枚举和统计的操作了。具体看代码吧….写的不清楚的地方欢迎评论指正。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
LL dp[20][50][3000];
int d[20],hash[3000];
const int mod = 2520;
int gcd ( int a ,int b )
{
return !b?a:gcd(b,a%b);
}
void init ( )
{
memset ( dp , -1 , sizeof ( dp ) );
int cc = 0;
for ( int i = 1 ; i <= 8 ; i *= 2 )
for ( int j = 1; j <= 9 ; j*= 3 )
for ( int k = 1 ; k <= 5 ; k *= 5 )
for ( int t = 1; t <= 7 ; t *= 7 )
hash[i*j*k*t] = ++cc;
}
LL dfs ( int n , int lcm , int f , int r )
{
if ( !n ) return r%lcm==0;
int ll = hash[lcm];
if ( f && dp[n][ll][r] != -1 )
return dp[n][ll][r];
int x = f?9:d[n];
LL ret = 0;
for ( int i = 0 ; i <= x ;i++ )
ret += dfs ( n-1 , i==0?lcm:lcm*i/gcd(lcm,i) , i==x?f:1 , (r*10+i)%mod );
return f? dp[n][ll][r] = ret : ret;
}
LL solve ( LL x )
{
int cc = 0;
while ( x )
{
d[++cc] = x%10;
x /= 10;
}
return dfs ( cc , 1 , 0 , 0 );
}
int main ( )
{
int t;
LL a,b;
scanf ( "%d" , &t );
init ( );
while ( t-- )
{
scanf ( "%lld%lld" , &a , &b );
printf ( "%lld\n" , solve ( b ) - solve ( a-1 ) );
}
}