codefroces 55D Beautiful numbers
[Description]
美丽数是指能被它的每一位非0的数字整除的正整数。
[Input]
包含若干组数据,每组数据一行两个数n,m,表示求[n,m]之间的美丽数的个数。
[output]
对于每组数据输出一个答案,各占一行。
Input
1
1 9
Output
9
Input
1
12 15
Output
2
[Hit]
0 < n , m < 10^18
测试数据不超过100组
基本思路是用:dp[len][mod][lcm]表示<=len的长度中,此数为mod,各数位的最小公倍数为lcm的数的个数来进行记忆化搜索。方法和上一题类似。
但我们发现,len在[1,20]范围内,mod在[1,1^18]范围内,lcm在[1,2520]范围内。所以dp数组肯定超内存。
下面我们来进行内存优化:
假设这个数为a,各个数位的值分别为ai,那么我们发现lcm(ai) | a.
而[1,9]的最小公倍数是2520.那么lcm(ai) | 2520, 所以lcm(ai) | (a%2520).
所以第二维大小我们可以从1^18降到2520,方法是%2520.
现在的dp数组的内存是20*2520*2520,还是很大。
然后我们再考虑:
我们发现某一个数的各个数位的数的最小公倍数最大是2520,而且只能是2520的公约数。而2520的公约数有48个。所以第三维我们只用[50]的空间就行了。
方法是用Hash进行离散化。‘
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long lol; 7 int bit[21],ha[2520],len,cnt; 8 lol f[21][2521][51],ans; 9 int gcd(int x,int y) 10 { 11 if (!y) return x; 12 return gcd(y,x%y); 13 } 14 int lcm(int x,int y) 15 { 16 return x*y/gcd(x,y); 17 } 18 void has(int x,int l) 19 { 20 if (x>10) return; 21 if (ha[l]==0) 22 ha[l]=++cnt; 23 has(x+1,lcm(l,x)); 24 has(x+1,l); 25 } 26 lol dfs(int pos,int pre_num,int pre_lcm,bool flag) 27 {int nxt_num,nxt_lcm,r,i; 28 lol s=0; 29 if (pos==0) 30 return pre_num%pre_lcm==0; 31 if (flag&&f[pos][pre_num][ha[pre_lcm]]!=-1) 32 return f[pos][pre_num][ha[pre_lcm]]; 33 if (flag) r=9; 34 else r=bit[pos]; 35 for (i=0;i<=r;i++) 36 { 37 nxt_num=pre_num*10+i; 38 nxt_num%=2520; 39 if (i) 40 nxt_lcm=lcm(pre_lcm,i); 41 else nxt_lcm=pre_lcm; 42 s+=dfs(pos-1,nxt_num,nxt_lcm,flag||(i<r)); 43 } 44 if (flag) 45 f[pos][pre_num][ha[pre_lcm]]=s; 46 return s; 47 } 48 lol solve(lol x) 49 { 50 len=0; 51 while (x) 52 { 53 len++; 54 bit[len]=x%10; 55 x/=10; 56 } 57 return dfs(len,0,1,0); 58 } 59 int main() 60 {lol l,r; 61 int T; 62 cin>>T; 63 has(2,1); 64 memset(f,-1,sizeof(f)); 65 while (T--) 66 { 67 scanf("%I64d%I64d",&l,&r); 68 ans=solve(r)-solve(l-1); 69 printf("%I64d\n",ans); 70 } 71 }