链接:
http://ac.jobdu.com/problem.php?pid=1491
题目描述:
给定正整数N,函数F(N)表示小于等于N的自然数中1和2的个数之和,例如:1,2,3,4,5,6,7,8,9,10序列中1和2的个数之和为3,因此F(10)=3。输入N,求F(N)的值,1<=N<=10^100(10的100次方)若F(N)很大,则求F(N)mod20123的值。
思路:
这题的数据量很大,首先肯定是要用字符串数组来存,然后从最低位开始,逐位分析,该位出现的次数是多少。
以abcdef为例,假如我们现在要求1在所有不超过它的自然数中出现的次数。
我们从最后一位开始,也就是f,此时f的高位是abcde,低位为0。那么这一位是1的数一共出现了多少次呢,需要看f与1的关系。
这个关系在最后一位不好叙述,我们以d为例,它的高位是abc,低位是ef,此时factor=100。
我们可以仔细想想,如果d=0,说明此位为1的数有abc*100。
如果d==1,说明此位为1的数有abc*100加上ef+1
如果d>1,那么就直接用(abc+1)*100就可以了
这样会计算重复的数,但是这是没有关系的,因为计算的是所有1的个数。
#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
#define mod 20123
string num;
int count(int x){
int k = num.length() - 1;
int factor = 1;
int ans = 0;
while (k >= 0){
int lownum = 0;
int highnum = 0;
int currnum = 0;
for (int i = 0; i < k; i++){ //从最低位开始,计算它的高位和低位
highnum = (highnum * 10 + (num[i]-'0')) % mod;
cout << "highnum = " << highnum << endl;
}
for (int i = k + 1; i < num.length(); i++){
lownum = (lownum * 10 + (num[i]-'0')) % mod;
cout << "lownum = " << lownum << endl;
}
currnum = num[k] - '0';
if (currnum < x){ //下面是分情况,看当前位是否大于1或者2
ans = (ans + (highnum * factor) % mod) % mod;
}
else if (currnum == x){
ans = (ans + (highnum * factor) % mod + lownum + 1) % mod;
}
else if (currnum > x){
ans = (ans + ((highnum + 1) * factor) % mod) % mod;
}
factor = (factor * 10) % mod;
k--;
}
return ans;
}
int main(){
while (cin>>num){
int ans = (count(1) + count(2)) % mod;
printf("%d\n", ans);
}
return 0;
}