题意:给出一个区间[a,b],求里面的数中共出现多少个0。

最初我和小T讨论的结果是逐位计算,先算个位,再算十位……不过TLE了。后来参考扯男的思路,在此基础上又改进了一下。代码中pow[]就是10的次方,g[i]表示所有 i 位数中含有多少个0(不过这个数组被我砍掉了),s[i]就是g[]的前 i 项和。f[i]不好讲解,例:f[2]表示从00、01……到99中有多少个0,f[3]表示从000、001、……到999中有多少个0。

说到思路嘛,拿35017做例子吧。在cal()中,首先计算的是[0,9999]、[10000,29999]两部分,根据已经打好表的数组可直接求。然后再从次高位到最低位循环,如果该位不为0,如5,就加上[31000,34999](后面位的0)、[30001,30999](当前位的0)两部分;如果为0,只需加上[35000,35017](当前位的0)一部分。

 1 #include <cstdio>
 2 #define N 19
 3 typedef long long llg;
 4 llg pow[N],f[N],s[N];
 5 int d[N];
 6 void init()
 7 {
 8     pow[0] = 1;
 9     pow[1] = 10;
10     s[0] = 1;
11     f[1] = s[1] = 1;
12     for(int i = 2; i < 13; i++)
13     {
14         pow[i] = 10*pow[i-1];
15         f[i] = i * pow[i-1];
16         s[i] = s[i-1]+9*f[i-1];
17     }
18 }
19 int dvid(llg n)
20 {
21     int f;
22     if(!n)
23     {
24         d[0] = 0;
25         return 1;
26     }
27     for(f = 0; n; n/=10)
28         d[f++] = n%10;
29     return f;
30 }
31 llg cal(llg n)
32 {
33     if(n < 0) return 0;
34     llg ans;
35     int len = dvid(n);
36     ans = s[len-1]+(d[len-1]-1)*f[len-1];
37     n %= pow[--len];
38     for(; len; len--)
39     {
40         if(d[len-1])
41             ans = ans + d[len-1]*f[len-1] + pow[len-1];
42         else ans += n+1;
43         n %= pow[len-1];
44     }
45     return ans;
46 }
47 int main()
48 {
49     llg m,n;
50     init();
51     while(scanf("%I64d%I64d",&m,&n),m>=0)
52     {
53         printf("%I64d\n",cal(n)-cal(m-1));
54     }
55     return 0;
56 }

做这题时遇到两个问题,一个是dvid(0)时出错,不过很好纠正;另一个是cal一位数时出错,为了纠正它,只好给s[0]赋了一个实际上并不正确的值。