数位DPCSU 1642: Problem B

举报
用户已注销 发表于 2021/11/19 03:36:58 2021/11/19
【摘要】 目录 一,数位DP 二,OJ实战 CSU 1642: Problem B ACdream - 1156 LCP SUM BZOJ 2757: Blinker的仰慕者 剑指 Offer 43. 1~n 整数中 1 出现的次数 一,数位DP 有一类奇特的DP问题,对象空间是一个整数集,而解空间是把每一位作为一维,...

目录

一,数位DP

二,OJ实战

CSU 1642: Problem B

ACdream - 1156 LCP SUM

BZOJ 2757: Blinker的仰慕者

剑指 Offer 43. 1~n 整数中 1 出现的次数


一,数位DP

有一类奇特的DP问题,对象空间是一个整数集,而解空间是把每一位作为一维,在这样的高维空间内完成DP,这类问题叫数位DP

 

二,OJ实战

CSU 1642: Problem B

题目:

Description

已知两个正整数a和b,求在a与b之间(包含a和b)的所有整数的十进制表示中1出现的次数。

Input

多组数据(不超过100000组),每组数据2个整数a,b.(1≤a,b≤1000000).

Output

每组数据的答案占一行。

Sample Input

1 10
10 100
2 1

Sample Output


  
  1. 2
  2. 20
  3. 1

思路:基本的数位DP

代码:


  
  1. #include <iostream>
  2. #include<stdio.h>
  3. using namespace std;
  4. int g(int n)//n有多少个1
  5. {
  6. int r = 0;
  7. while (n)r += (n % 10 == 1), n /= 10;
  8. return r;
  9. }
  10. int f(int n)//1到n有多少个1
  11. {
  12. if (n == 0)return 0;
  13. return (n + 9) / 10 + f(n / 10) * 10 - g(n / 10)*(9 - n % 10);
  14. }
  15. int main()
  16. {
  17. int a, b;
  18. while (scanf("%d%d",&a,&b)!=EOF)
  19. {
  20. if (a > b)a ^= b ^= a ^= b;
  21. printf("%d\n", f(b) - f(a - 1));
  22. }
  23. return 0;
  24. }

ACdream - 1156 LCP SUM

题目:

long long ans = 0;
for(int i = 1; i <= n; i ++)
     ans += lcp(i - 1,i)

给出n,求ans

lcp(i - 1,i)指的是两个数字的最长公共前缀,比如lcp(2,3) = 0,lcp(10,11) = 1,lcp(12345,12346) = 1234

Input

多组数据,每组数据一个n (1 <= n <= 10^9)

Output

对于每个n,输出一行,求得的ans

Sample Input


  
  1. 10
  2. 11
  3. 12
  4. 22
  5. 100

Sample Output


  
  1. 0
  2. 1
  3. 2
  4. 13
  5. 405

Hint

数据组数<=50000

 

需要注意的是109-110这种情况,lcp(109,110)=lcp(10,11)

本质上是数位DP

代码:


  
  1. #include<iostream>
  2. using namespace std;
  3. long long lcpsum(int n)
  4. {
  5. if (n < 10)return 0;
  6. long long l = n / 10;
  7. return (l - 1)*l / 2 * 9 + l * (n % 10) + lcpsum(l);
  8. }
  9. int main()
  10. {
  11. int n;
  12. while (cin >> n)cout << lcpsum(n) << endl;
  13. return 0;
  14. }

BZOJ 2757: Blinker的仰慕者

题目:

Description
Blinker 有非常多的仰慕者,他给每个仰慕者一个正整数编号。而且这些编号还隐藏着特殊的意义,即编号的各位数字之积表示这名仰慕者对Blinker的重要度。 现在Blinker想知道编号介于某两个值A,B之间,且重要度为某个定值K的仰慕者编号和。 

Input

输入的第一行是一个整数N,表示Blinker想知道的信息个数。 
接下来的N行,每行有三个数,A,B,K。表示 Blinker想知道编号介于A和B之间的,
重要度为K的仰慕者的编号和。           

Output
输出N行,每行输出介于A和B之间,重要度为 K的仰慕者编号和。结果可能很大,
模上20120427。 

Sample Input

3
1 14 4
1 30 4
10 60 5
0<=K<=10^18

Sample Output

18
40
66
【样例解释】
第一组样例中,在 1到14之间各位数字之积等于 4的有 4和 14,故编号和为18。

HINT

【数据范围】 

对于20%的数据,保证:  2<=A<=B<=1000000000,1<=N<=30 

对于50%的数据,保证:2<=A<=B<=1000000000000000000,1<=N<=30 

对于100%的数据,保证:  2<=A<=B<=1000000000000000000,1<=N<=5000

 

代码:


  
  1. #include<iostream>
  2. #include<map>
  3. #define ll long long
  4. using namespace std;
  5. int p = 20120427;
  6. typedef pair<ll, ll> pairll;
  7. map<pairll, ll>maps;
  8. map<pairll, ll>::iterator it;
  9. ll num1(ll n, ll k)//n到2n-1中积为k的个数,n为10的幂
  10. {
  11. if (n == 1)return k == 1;
  12. ll sum = 0;
  13. for (int i = 1; i <= 9; i++)if (k%i == 0)sum += num1(n / 10, k / i);
  14. return sum % p;
  15. }
  16. ll num2(ll b, ll k)//1到b中积为k的个数
  17. {
  18. if (b <= 0 || k == 0)return 0;
  19. if (b < k)return 0;
  20. if (b < 10)return 1;
  21. it = maps.find(make_pair(b, k));
  22. if (it != maps.end())return it->second;
  23. ll m = 1;
  24. while (m * 10 <= b)m *= 10;
  25. ll h = b / m, sum = num2(m - 1, k);
  26. for (int i = 1; i < h; i++)if (k%i == 0)sum += num1(m, k / i);
  27. if (k%h == 0)sum += num2(b%m, k / h) - num2(m / 10 - 1, k / h);
  28. it = maps.begin();
  29. pairll temp = make_pair(b, k);
  30. maps.insert(it, make_pair(temp, sum%p));
  31. return sum % p;
  32. }
  33. ll ans(ll b, ll k)//1到b中积为k的所有数的和
  34. {
  35. if (b <= 0 || k == 0)return 0;
  36. if (b < 10)return (k < 10)*k;
  37. ll m = 1;
  38. while (m * 10 <= b)m *= 10;
  39. ll h = b / m, sum = 0;
  40. sum = ans(m - 1, k);
  41. for (int i = 1; i < h; i++)if (k%i == 0)sum += num1(m, k / i)*i*m%p + ans(m - 1, k / i) - ans(m / 10 - 1, k / i);
  42. if (k%h == 0)sum += (num2(b%m, k / h) - num2(m / 10 - 1, k / h))*h*m%p + ans(b%m, k / h) - ans(m / 10 - 1, k / h);
  43. return sum % p;
  44. }
  45. int main()
  46. {
  47. int n;
  48. ll a, b, k;
  49. cin >> n;
  50. while (n--)
  51. {
  52. cin >> a >> b >> k;
  53. cout << (ans(b, k) + p - ans(a - 1, k)) % p << endl;
  54. }
  55. return 0;
  56. }

数据太大了会超时

 

剑指 Offer 43. 1~n 整数中 1 出现的次数

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

 

示例 1:

输入:n = 12
输出:5
示例 2:

输入:n = 13
输出:6
 

限制:

1 <= n < 2^31

思路:

把最后一位的1的个数计算出来,然后问题规约为子问题。

 


  
  1. class Solution {
  2. public:
  3. int countDigitOne(int n) {
  4. if(n<10)return (n>=1)?1:0;
  5. int ans=countDigitOne(n/10-1)*10+(n-1)/10+1;
  6. int ans2=0,m=n/10;
  7. while(m){
  8. if(m%10==1)ans2++;
  9. m/=10;
  10. }
  11. return ans+ans2*(n+1-n/10*10);
  12. }
  13. };

 

文章来源: blog.csdn.net,作者:csuzhucong,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/nameofcsdn/article/details/113071516

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。