UVa 1640 - The Counting Problem(数论)

链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4515

题意:

给出整数a、b,统计a和b(包含a和b)之间的整数中,数字0,1,2,3,4,5,6,7,8,9分别出现了多少次。1≤a,b≤1e8。

分析:

解决这类题目的第一步一般都是:令f(n,d)表示0~n中数字d出现的次数,则所求的就是f(b,d)-f(a-1,d)。
例如,要统计0~234中4的个数,可以分成几个区间:

范围                    模板集
0~9                    *
10~99                **
100~199            1**
200~229            20*,21*,22*
230~234            230,231,232,233,234

上表中的“模板”指的是一些整数的集合,其中字符“*”表示“任意字符”。例如,1**表示以1开头的任意3位数。
因为后两个数字完全任意,所以“个位和十位”中每个数字出现的次数是均等的。
换句话说,在模板1**所对应的100个整数的200个“个位和十位”数字中,0~9各有20个。
而这些数的百位总是1,因此得到:模板1**对应的100个整数包含数字0,2~9各20个,数字1有120个。
这样,只需把0~n分成若干个区间,算出每个区间中各个模板所对应的整数包含每个数字各多少次,就能解决原问题了。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int UP = 10;
 5 int pow10[UP], amt[UP];
 6 
 7 int f(int n, int d) {
 8     int res = 0;
 9     char s[99];
10     sprintf(s, "%d", n);
11     int len = strlen(s);
12 
13     for(int i = 1; i < len; i++) {
14         if(i == 1) res++;
15         else {
16             res += 9 * amt[i-1];
17             if(d > 0) res += pow10[i-1];
18         }
19     }
20 
21     int pre = 0;
22     for(int i = 0; i < len; i++) {
23         int L = 0, R = s[i]-'0';
24         if(i == 0 && len > 1) L = 1;
25         for(int digit = L; digit < R; digit++) {
26             res += amt[len-1-i] + pre * pow10[len-1-i];
27             if(digit == d) res += pow10[len-1-i];
28         }
29         if(s[i]-'0' == d) pre++;
30     }
31     return res + pre;
32 }
33 
34 int main() {
35     pow10[0] = 1;
36     for(int i = 1; i < UP; i++) {
37         pow10[i] = pow10[i-1] * 10;
38         amt[i] = pow10[i] * i / 10;
39     }
40     int a, b;
41     while(scanf("%d%d", &a, &b) && a) {
42         if(a > b) b += a, a = b-a, b -= a;
43         printf("%d", f(b,0) - f(a-1,0));
44         for(int i = 1; i < 10; i++) printf(" %d", f(b,i) - f(a-1,i));
45         printf("\n");
46     }
47     return 0;
48 }

猜你喜欢

转载自www.cnblogs.com/hkxy125/p/9631697.html