题意:求l,r范围内能被自身所有非0digits整除的数的个数,范围是1~9e18
题解:
一个个digit判断很难搞,用lcm直接判断就会简单很多,如果所有种类的digit都有,那么1~9的lcm是2520,就算数少一点也必定是2520可以整除的数,那么根据这个进行状态转移。
因为假设对于数字num判断是否符合条件只需要判断num是否可以整除一个小于等于2520的数就可以,所以转移的时候可以把num%2520进行记录,那么状态转移就只需要记录三个量,数字长度,到目前为止num的大小,到目前为止所有digit的lcm的大小在根据新加的digit转移就可以,可以直接记忆化搜索dfs(len,num,lcm,fg),fg用来判断此位数字是否有限制。
开始对记忆化搜索理解不深一直在纠结应该怎样记录对于位数小于当前数的数的个数,其实记忆化搜索的时候根据fg完全可以直接记录下来,一点都不麻烦,具体看代码
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <iostream> 5 #include <algorithm> 6 #include <map> 7 #include <vector> 8 #include <queue> 9 using namespace std; 10 #define EPS 1e-10 11 typedef long long ll; 12 const int maxn = 1e5 + 10; 13 const int INF = 1e9 ; 14 const double eps = 1e-8; 15 const int mod = 2520; 16 int mp1[50],mp2[3000],dis[20]; 17 ll dp[20][2550][50]; 18 void init(){ 19 memset(dp,-1,sizeof(dp)); 20 int cnt = 0; 21 for(int i = 1; i <= 2520;i++){ 22 if(2520 % i == 0){ 23 mp1[++cnt] = i; 24 mp2[i] = cnt; 25 } 26 } 27 } 28 int gcd(int a,int b){ 29 return b == 0?a:gcd(b,a%b); 30 } 31 int getlcm(int u,int v){ 32 if(v == 0) return u; 33 return u*v/gcd(u,v); 34 } 35 ll dfs(int len,int num,int lcm,int fg){ 36 if(len == 0) return num % mp1[lcm] == 0; 37 if(!fg && -1 != dp[len][num][lcm]) return dp[len][num][lcm]; 38 int ed = fg?dis[len]:9; 39 ll ans = 0; 40 for(int i = 0;i <= ed;i++){ 41 int x = getlcm(mp1[lcm],i); 42 ans += dfs(len - 1,(num*10+i) % mod,mp2[x],fg&&i==ed); 43 } 44 if(!fg) dp[len][num][lcm] = ans;//这里直接记录没有限制情况下的值提高效率 45 return ans; 46 } 47 ll solve(ll u){ 48 int pos = 0; 49 while(u > 0){ 50 dis[++pos] = u % 10; 51 u /= 10; 52 } 53 return dfs(pos,0,1,1); 54 } 55 int main(){ 56 // freopen("in.txt","r",stdin); 57 int T; 58 cin>>T; 59 init(); 60 while(T--){ 61 ll l,r; 62 scanf("%I64d%I64d",&l,&r); 63 printf("%I64d\n",solve(r) - solve(l-1)); 64 } 65 return 0; 66 }