1049 Counting Ones (30分)
思路
完全的数学题。
看到N的取值的那刻我就放弃用枚举手段了,所以也没有验证会不会超时。大概是会的,不然这题也太没难度了。
言归正传。这道题的重点是找到一个计算方法。方法正确的话,步数会下降到可以人类手算的程度——换句话说就是可以忽略不计(迫真奥数题)。
那么,怎么统计呢?
作为举例,假设这个数是9位数,也就是xab cde fgh。
很显然,对于任何一个9位数的n,我们要计算的范围首先必然包括1~99 999 999。
对于它们,首先范围变成0~99 999 999,然后把所有数扩充成八位,也就是00 000 000到99 999 999。此时你会发现,所有由8个数字组成的数(任何一位的取值都是0到9)都在这个范围内了。这一堆数字中总共有100 000 000*8个数字,而1的出现概率即是十分之一,故0到99 999 999的数字范围内1出现了80 000 000次。
同样的,从k00 000 000到k99 999 999间,不考虑k的话,1也应该出现了80 000 000次。
所以,对于xab cde fgh,不考虑x位的话,从0到x00 000 000,1出现了x8pow(10,8)次。
再来看x位。显然如果x不是1(大于等于2),那么x位1就出现了100 000 000到199 999 999共pow(10,8)个数,每个各1次,如果x就是1,那就是100 000 000到1ab cde fgh共ab cde fgh+1次。
所以,现在,我们已经搞清了0~x00 000 000间1的出现次数和x所在位1的出现次数。只需要再加上x00 000 000到xab cde fgh间除了x所在位外1的出现次数。
而这很显然和0~ab cde fgh间1的出现次数是相同的。
所以,只需要把以上代码写成循环,然后现在把n的最高位去掉、把位数下调,然后再不断进行即可。
代码
#include<iostream>
#include<math.h>
using namespace std;
int main()
{
long int n;//虽然其实int就足以放下题设n的最大值,但由于下方的那个循环条件退出时pow(10,i)是比n高一位的,而pow(10,i)的数据类型似乎与与之比较的n是相同的,故若n为int会溢出出错。(这是我的推测。不过确定的是用int最后一个测点会报答案错误。)
cin >> n;
int count = 0;
int i = 0, k = 0;
while (pow(10,i) <= n)//此处的条件
{
i++;
}
i--;
while(i >= 0)
{
k = pow(10, i);
count += (n / k)*i * k / 10;
if (n / pow(10, i) >= 2)
count += k;
else
count += n - k + 1;//注意这一句。后面会说明。
n = n % k;
while (pow(10, i) > n)
i--;//注意如果不做这个循环直接i--的话,上面那一句将有可能得到负数结果,使得答案错误。当然也可以通过把else改成else if(n / pow(10, i)==0)规避。
}
cout << count;
return 0;
}
题目
The task is simple: given any positive integer N, you are supposed to count the total number of 1’s in the decimal form of the integers from 1 to N. For example, given N being 12, there are five 1’s in 1, 10, 11, and 12.
Input Specification:
Each input file contains one test case which gives the positive N (≤2
30
).
Output Specification:
For each test case, print the number of 1’s in one line.
Sample Input:
12
Sample Output:
5