电工实习终于结束了,考试难的一比。
题意
给出一个序列:
11212312341234512345612345671234567812345678912345678910123456789101112345678910…
给一个序号i,求出这个位置的数字是多少。
序号从1开始。
输入输出
Input
The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by one line for each test case. The line for a test case contains the single integer i (1 ≤ i ≤ 2147483647)
Output
There should be one output line per test case containing the digit located in the position i.
分析
这个题和前几天做的两个题大体思路类似,把这个序列进行几个层次的分割,然后一层一层缩小目标的范围。
对这个题目来说,先找出这个位置处于哪一个从12345…n序列,那么就需要预处理出以任意数字x结尾的递增序列的长度,并搞出前缀和。之后用二分法求出目标递增序列的结尾n。
确定目标位置处于哪一个递增序列后,要确定目标位置处于哪一个位置,最麻烦的地方在于随着数字增大,一个数字所占位数也会增多,因此先确定目标位置处于几位数,同样的方法,预处理不同位数的信息,二分法求出所在数字位数,以及在这些数字里它处于第几位,之后求解就用这个位置除以位数,对位数取余就行了。
错了一些地方调了好久。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mxn 100010
#define mxm 7
long long d[mxn];
long long c[mxn];
long long num[mxm];
long long tens[]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000LL,10000000000LL};
long long n;
int calc_len(int in){
int ret=0;
while(in){
++ret;
in/=10;
}
return ret;
}
void set_d(){
num[0]=0;
for(int i=1;i<mxm;++i){
num[i]=9;
for(int j=2;j<=i;++j)
num[i]*=10;
num[i]*=i;
num[i]+=num[i-1];
}
d[0]=c[0]=0LL;
for(int i=1;i<mxn;++i) d[i]=d[i-1]+calc_len(i);
for(int i=1;i<mxn;++i) c[i]=c[i-1]+d[i];
}
int main(){
//freopen("data.txt","w",stdout);
set_d();
int cs,CS=9000;
scanf("%d",&cs);
while(cs--){
//n=++CS;
scanf("%lld",&n);
long long l=1,r=mxn;
while(l+1<r){
long long m=(l+r)>>1;
if(c[m]<=n) l=m;
else r=m;
}
long long dist=n-c[l];
if(!dist){
printf("%d\n",(int)(l%10));
continue;
}
l=0,r=mxm;
while(l+1<r){
long long m=(l+r)>>1;
if(num[m]<dist) l=m;
else r=m;
}
dist=dist-num[l];
int ta=dist/r;
int tb=dist%r;
if(!tb){
printf("%lld\n",(ta-1+tens[r])%10);
continue;
}
//cout<<"ta "<<ta<<endl;
tb=r-tb;
long long tem=ta+tens[r];
for(int i=0;i<tb;++i) tem/=10;
printf("%d\n",(int)(tem%10));
}
return 0;
}