51nod- 【1042 数字0-9的数量 】

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1042

题目:
1042 数字0-9的数量
基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题
 
给出一段区间a-b,统计这个区间内0-9出现的次数。
 
比如 10-19,1出现11次(10,11,12,13,14,15,16,17,18,19,其中11包括2个1),其余数字各出现1次。
Input
两个数a,b(1 <= a <= b <= 10^18)
Output
输出共10行,分别是0-9出现的次数
Input示例
10 19
Output示例
1
11
1
1
1
1
1
1
1
1

题解:

求区间[a,b]各位数字出现次数并打印输出。

因为数据是10^18,暴力会T。
所以应该要找出出现的规律,优化算法。

从个位开始分析各个数位。
不妨把当前数位右侧的数都当作0来统计:

1、当前数位(cur):那么当前数位从1开始到cur,当前数位每增大1,这个数字出现次数是当前数位的权值value(如从11增大到12时,个位上1出现了value = 1次;从110增大到120时,十位上的1出现了value = 10次)

2、当前数位左边的数(res)也会影响当前数位:res每增大1,当前数位的数都会从0-9走遍历一遍,其中每次遍历在0-9的每个数字上产生的次数为value次。(如12300这个数,res = 123 ,value = 10,十位上的0-9产生的总次数均为 res * value = 1230 次)

3、当前数位还会影响当前数位左边的数:当前数位从0开始到cur,当前数位每增大1,当前数位左边的每个数均出现value次,所以总共会出现value * (cur + 1)次。(如12340这个数,cur = 4,value = 10。从12300增大到12349时,cur左边的1、2、3这三个数均会出现 value * (cur + 1) = 50次)

4、如果当前数位左边还有数,当前数位左移,继续统计。

AC代码:
 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 typedef long long ll;
 6 
 7 void dfs(ll res , ll value , ll LLSum[])
 8 {
 9     ll cur = res % 10;
10     res /= 10;
11     ll t = res;
12     for (int i = 1 ; i <= cur ; i++) LLSum[i] += value;
13     for (int i = 0 ; i < 10 ; i++) LLSum[i] += res * value;
14     while (t) {
15         LLSum[t%10] += (cur + 1) * value;
16         t /= 10;
17     }
18     if (res) dfs(res-1,value * 10,LLSum);
19     return;
20 }
21 
22 int main()
23 {
24     ll a,b;
25     ll Suma[10],Sumb[10];
26     memset(Suma,0,sizeof(Suma));
27     memset(Sumb,0,sizeof(Sumb));
28     cin >> a >> b;
29     dfs(a-1,1,Suma);
30     dfs(b,1,Sumb);
31     for (int i = 0 ; i < 10 ; i++) {
32         cout << Sumb[i] - Suma[i] << endl;
33     }
34     return 0;
35 }
其实只要撸清各位数字之间的影响就可以了。
原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/8312713.html