hiho 180
题目1 : Nature Numbers
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
Consider the following sequence S which is constrcuted by writting nature numbers one by one: “012345678910111213…”.
The first digit of S, S[0], is 0. The second digit S[1] is 1. And the 11th digit S[10] is 1.
Given an integer N, can you find the digit S[N]?
输入
An integer N. (0 <= N <= 1018)
输出
Digit S[N].
样例输入
17
样例输出
3
题目分析:一个直观的解法是从1, 2, 3, … 开始一个数一个数枚举。一开始count=0,保存位数之和。
假设当前枚举到K,我们就把count加上K的位数。如果这时count大于等于N,我们就知道第N位应为K的倒数第N-K+1位。
考虑到N=10^18时,K至少大于10^16,这个算法肯定会超时。
一种优化的思路是不要一个数一个数枚举,而是一批数一批数枚举:每次枚举所有的一位数、两位数、三位数……
一位数有10个(包括0),两位数有90个,三位数有900个……
假设当前枚举到K位数,我们就把count加上(K * K位数的个数)。如果这时count大于等于N,我们就知道第N位是一个K位数的某一位。同时要记录一下count的前一个值pre,pre表示K位数的第一个数的第一位的下标。然后我们可以还原出这个n对应的是K位数的第几个数data,即 data = pow(K-1)+ (n - pre)/k。其中pow(k-1)表示的是K位数的第一个数,(n-pre)/k对应的是第几个K位数。然后利用 (n-pre)% k + 1得到n是data的第几位。最后将data进行分解得到答案。
注意要用 long long
#include <iostream>
using namespace std;
long long pow(long long x)
{
if(x == 0) return 0;
long long ans = 1;
for (long long i = 0; i < x; ++i)
ans *= 10;
return ans;
}
int main()
{
long long n;
cin>>n;
long long count = 0;
long long pre = 0;
long long i;
for ( i = 1 ; ; ++i){
if ( count <= n){
pre = count;
count += i*(pow(i) - pow(i-1));
}
else break;
}
--i;
long long k = i;
long long data = pow(i-1) + ((n - pre)/i);
long long flag = (n - pre)% i + 1;
long long res = 0;
while(data){
res = data%10;
data /= 10;
if(k == flag) break;
k--;
}
cout<<res<<endl;
return 0;
}