题意:
数字满足的条件是该数字可以被它的每一位非零位整除。
思路:
数位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; }