题目:
输入正整数N(1=<N<=10^100(10的100次方)),输出F(N) mod20123。F(N)表示小于等于N的自然数中1和2的个数之和。例如:1,2,3,4,5,6,7,8,9,10,11序列中1和2的个数之和为5,因此F(11)=5。
思路:
N用string存储,先求出F(N[0]),再求出F(N[0]N[1]),再求出F(N[0]N[1]N[2]),……,最后求出F(N)。而由前一个F求出后一个F’,用公式: F’=(F-cons)*10+ cons*(newChar+1) + preNum*2 + min(newChar,2)。 假设由F(N[0]…N[i])求F(N[0]…N[i]N[i+1]),例如由F(52)求F(526):
- F: 为F(N[0]…N[i])。 F(52)
- F’: 为F(N[0]…N[i]N[i+1])。 F(526)
- cons: N[0]—N[i]中1和2的个数。 1
- preNum: N[0]…N[i]这个数字字符串组成的数字。 52
- newChar: 新的那个数字符号,即N[i+1]。 6
公式分为4部分:
- (F-cons)*10 : F(52)中的数(如1,2,10,11,…,29,31,32,41,42,51),变成F(526)后,都会依次和0~9组合(如10~19,20~29,100~109,…,510~519),所以乘以10。又因为preNum本身不一定能组成preNum0~9,所以交给第二项来单独算(如52只能算520~526)。
- cons*(newChar+1): 见上。
- preNum*2 : F(526)中,个位数也新增加了1和2,为每10个数中,就必有两个数的个位为1或2。而526共有preNum(即52)个整10(0~519),所以用preNum*2。至于520~526个位数有几个1和2,要交给第四项单独计算。
- min(newChar,2): 见上。
过程:
1.在使用"?:"时,若运算符若非直接赋值,要括号括起来。因为其优先级很低。如:cons=cons+((newChar==1||newChar==2)?1:0);
不可cons=cons+(newChar==1||newChar==2)?1:0; 否则会先算加号。
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
int min(int a,int b){
return a<b?a:b;
}
int main(){
string N;
int len,i;
int F,preNum,cons,newChar;
while(cin>>N){
len=N.length();
F=cons=preNum=newChar=0;
for(i=0;i<len;i++){ //由F(N[0])求到F(N[0]N[1]...N[len-1])
newChar=N[i]-48;
F=(F-cons)*10 + cons*(newChar+1) + preNum*2 + min(newChar,2); //套公式计算
preNum=(preNum*10+newChar)%20123;
cons=cons+((newChar==1||newChar==2)?1:0); //?:运算符若非直接赋值,要括号括起来。因为其优先级很低。
F=F%20123;
}
printf("%d\n",F); //输出结果
}
return 0;
}