题意:
数字满足的条件是该数字可以被它的每一位非零位整除。
思路:
数位dp的核心思路就是找到判断这个数的满足条件的方法,这个题的条件就是这个数要能被自己的每一个非零位整除,也就是应该被每一非零位的最小公倍数整除,而的最小公倍数是
,现在就可以拟确定dfs的参数和dp的状态了。
,
表示当前处理到第
位,
表示当前数值
的值,
表示当前的最小公倍数,
表示当前是否有范围限制。
临界点也是最重要的部分,当时,返回
。
数位dp的状态就应该是,即
位数符合条件的数的个数,但
和
最大都是
,也就是说数位
要开
,开不了这么大。但一维里面的很多空间是多余的,因为
的最小公倍数有
种,所以将
离散化到
的区间,这样就可以开
的数组了。
MyCode:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 607,mod=2520;
inline ll read() {
ll s = 0, w = 1;
char ch = getchar();
while (ch < 48 || ch > 57) {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= 48 && ch <= 57)
s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * w;
}
ll dis[49]={0,1,2,3,4,5,6,7,8,9,10,12,14,15,18,20,21,24,28,30,35,36,40,42,45,56,60,63,70,72,84,90,105,120,
126,140,168,180,210,252,280,315,360,420,504,630,840,1260,2520};
int mp[2521];
ll dp[21][2521][49],inf[21];
int digit[20];
int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
ll dfs(int len,int sum,int lcm,bool limit) {
if(!len) return sum%lcm==0;
int tmp=mp[lcm];
ll ans=0;
if(!limit&&dp[len][sum][tmp]!=-1) return dp[len][sum][tmp];
int endi=(limit?digit[len]:9);
ans+=dfs(len-1,sum,lcm,limit&&0==endi);
for(int i=1;i<=endi;++i)
ans+=dfs(len-1,(sum+i*inf[len])%mod,lcm/gcd(lcm,i)*i,limit&&i==endi);
if(!limit) dp[len][sum][tmp]=ans;
return ans;
}
ll solve(ll x) {
int len=0;
while(x) {
digit[++len]=x%10;
x/=10;
}
return dfs(len,0,1,true);
}
int main() {
for(int i=1;i<=48;++i) mp[dis[i]]=i;
inf[1]=1;
for(int i=2;i<=19;++i) inf[i]=inf[i-1]*10%mod;
memset(dp,-1,sizeof dp);
int t=read();
while(t--) {
ll l=read(),r=read();
printf("%I64d\n",solve(r)-solve(l-1));
}
return 0;
} 
京公网安备 11010502036488号