【数学】8.30题解-count数页码

count

洛谷p1836

题目描述

一本书的页码是从 1-n 编号的连续整数: 1, 2, 3, ... , n。请你求出全部页码中
所有单个数字的和,例如第 123 页,它的和就是 1+2+3=6。

输入输出

输入
一行为 n(1 <= n <= 10^9)。
输出
一行,代表所有单个数字的和。

样例

样例输入

3456789

样例输出

96342015

说明

时间限制 1s/testcase
空间限制 32MB

思路

鉴于 n 可以达到 10^9,直接模拟是不可取的
逐位递推,发现规律.

  1. 对于10^7以内的数据,直接模拟暴力可以过
for(int i=1; i<=n; i++)
	ans+=solve(i);
inline int solve(int x) {
        int an=0;
        while(x) {
		an=an+x%10;
		x/=10;
	}
}
  1. 可以先用暴力算出n=1e1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9时的答案
  2. 对于大于1e7的数据我们可以逐位来算
    举个较小的例子:7654
    第一位:7 ;
    (1) 则1~6一定都在第一位出现了1e3次;
    (2) 7出现了654+1次
    (3) 再加上7*ans(1e3时的答案)
    (4) 然后去掉这一位,原本的第二位,变成了现在的第一位

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<ctime>

using namespace std;
typedef long long ll;
int n;
ll ans;
ll a[20];
inline int solve(int x) {
	int an=0;
	while(x) {
		an=an+x%10;
		x/=10;
	}
	return an;
}
int main() {
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%d",&n);
	if(n<=1e7) {
		for(int i=1; i<=n; i++)
			ans+=solve(i);
		printf("%d",ans);
		return 0;
	}
	a[1]=45;
	a[2]=900;
	a[3]=13500;
	a[4]=180000;
	a[5]=2250000;
	a[6]=27000000;
	a[7]=315000000;
	a[8]=3600000000LL;
	if(n<1e8) {
		ll temp=1e7,pos=7;
		while(pos) {
			int k1=n/temp;
			k1--;
			ans+=(ll)temp*(ll)k1*(ll)(k1+1)/2;
			ans+=(ll)(k1+1)*(ll)a[pos];
			ans+=(ll)(k1+1)*(ll)(n%temp+1);
			n%=temp;
			temp/=10;
			pos--;
		}
		ans+=(ll)n*(ll)(n+1)/2;
		printf("%lld",ans);
		return 0;
	}
	if(n<1e9) {
		ll temp=1e8,pos=8;
		while(pos) {
			int k1=n/temp;
			ans+=(ll)temp*(ll)k1*(ll)(k1-1)/2;   //(1)
			ans+=(ll)(k1)*(ll)a[pos];            //(3)
			ans+=(ll)(k1)*(ll)(n%temp+1);        //(2)
			n%=temp;                             //(4)
			temp/=10;                            //(4)
			pos--;                               //(4)
		}
		ans+=(ll)n*(ll)(n+1)/2;
		printf("%lld",ans);
		return 0;
	}
	return 0;
}
posted @ 2017-08-30 17:30  bbqub  阅读(215)  评论(0编辑  收藏  举报