#51 D. Beautiful numbers (数位dp+离散化)

小咪咪 2022-06-17 03:44 297阅读 0赞

题目链接:

点击打开链接

http://codeforces.com/contest/55/problem/D

题意:

定义:Beautiful Numbers : 这个数能整除它的所有位上非零整数。问你[ l , r ] 之间的Beautiful Numbers的个数。

题解:

数位dp。

如果一个数能整除它的所有的非零数位,那么相当于它能整除个位数的最小公倍数。因此记忆化搜索中的参数除了len(当前位)和flag(是否达到上界),有一个pre_lcm表示前面的数的最小公倍数,判断这个数是否是Beautiful Numbers,还要有一个参数表示前面数,但是这个数太大,需要缩小它的范围。

解决方法:

缩小前面组成的数的范围。
可以发现所有个位数的最小公倍数是2520,假设当前的Beautiful Numbers是x,
那么 x % lcm{ dig[ i ] } = 0,
又 2520%lcm{ dig[ i ] } = 0。
那么x%2520%lcm{ dig[i] } = 0,x范围由9*10^18变为2520。

经过分析后可以设出dp[20][2520][2520],dp[ i ] [ j ][ k ]表示处理到i位,前面的数的最小公倍数为j,前面的数%2520为k。这样做居然会TLE…明显爆内存…

因为1~9组成的最小公倍数只有48个,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。一共48个。 可以离散化,这样数组就降到了dp[20][50][2520]。

AC代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int max_lcm=2520;
  5. int dig[25];
  6. int Hash[2525];
  7. ll dp[25][50][2525];
  8. ll gcd(ll a,ll b)
  9. {
  10. return b==0?a:gcd(b,a%b);
  11. }
  12. ll lcm(ll a,ll b)
  13. {
  14. return a/gcd(a,b)*b;
  15. }
  16. ll dfs(int len,int pre_lcm,int pre_num,int flag)
  17. {
  18. if(len==0)
  19. {
  20. return pre_num % pre_lcm ==0;
  21. }
  22. if(!flag && dp[len][ Hash[pre_lcm] ][pre_num]!= -1 )
  23. return dp[len][ Hash[pre_lcm] ][pre_num];
  24. int n = flag ? dig[len] : 9;
  25. ll res = 0;
  26. for(int i=0;i<=n;i++)
  27. {
  28. int now_num = (pre_num * 10 +i) % max_lcm;
  29. int now_lcm = pre_lcm;
  30. if(i) now_lcm = lcm(pre_lcm,i);
  31. res += dfs(len-1,now_lcm,now_num,flag&&i==n);
  32. }
  33. if(!flag){
  34. dp[len][Hash[pre_lcm]][pre_num] = res;
  35. }
  36. return res;
  37. }
  38. ll cal(ll num)
  39. {
  40. int len=0;
  41. while(num)
  42. {
  43. dig[++len]=num%10;
  44. num/=10;
  45. }
  46. return dfs(len,1,0,1);
  47. }
  48. int main()
  49. {
  50. //freopen("in.txt","r",stdin);
  51. int t;
  52. ll a,b;
  53. int cnt=0;
  54. for(int i=1;i<=2520;i++)
  55. {
  56. if(max_lcm%i==0){
  57. Hash[i]=++cnt;
  58. }
  59. }
  60. cin>>t;
  61. memset(dp,-1,sizeof(dp));
  62. while(t--)
  63. {
  64. scanf("%I64d %I64d",&a,&b);
  65. //cout<<cal(b)-cal(a-1)<<endl;
  66. printf("%I64d\n",cal(b)-cal(a-1));
  67. }
  68. return 0;
  69. }

发表评论

表情:
评论列表 (有 0 条评论,297人围观)

还没有评论,来说两句吧...

相关阅读