Sample Input
5
1
3
20
38
56
Sample Output
1
2
5
2
0
解题思路以及关键代码
核心思路是主要是二分
首先分析序列:s1=1,s2=12,s3=123,s4=1234,……s10=12345678910
序列 s1s2s3s4……s9s10,可以通过二分的方法,首先确定属于哪个s中;
同时a[i]记录第i位数序列的最大值,b[i]记录最后一个i位数所对应的序列长度
①确定k属于几位数。
②通过二分查找确定 k属于 i位数序列的第几个。
③最后在这个序列中查找k的位置。
全部代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;
long long a[20],b[20];
int func1()
{
a[0]=b[0]=0;
long long count = 9;
int i;
for(i=1;a[i-1]<=(long long)pow(10,18);i++)
{
a[i]=((b[i-1]+i)+(b[i-1]+count*i))*count/2; //s1s2....si共有多少位
b[i]=b[i-1]+count*i; //最后一个i位数所对应的序列长度
count*=10;
}
return i-1;
}
int main()
{
long long q;
scanf("%lld",&q);
long long n=func1();
while(q--)
{
long long k;
scanf("%lld",&k);
//确定 k 属于i位数
long long i=(long long)(lower_bound(a,a+n,k)-a);
for(int j=1;j<i;j++)
k=k - a[j];
//确定 k 属于i位数序列的第几个 b[i]中记录了个数
long long l=1;
long long r=(long long)(9*pow(10,i-1));
long long mid;
while(l<=r) //二分
{
mid=(l+r)/2;
long long tmp=((b[i-1]+i)+(b[i-1]+mid*i))*mid/2;
if(tmp>=k)
r=mid-1;
else
l=mid+1;
}
k-=((b[i-1]+i)+(b[i-1]+r*i))*r/2;
//从这个数所对应的序列中查找 k 的位置
long long len=1;
while(true)
{
long long tmp=9*pow(10,len-1)*len;
if(tmp>=k) break;
k=k - tmp;
len++;
}
long long num=(k+len-1)/len;
//减去前面num-1个len位数,在第num个数中继续搜索位置
k-=(num-1)*len;
//第num个len位数等于几
num=pow(10,len-1)+num-1;
//位于第几位
long long cnt=len-k+1;
for(long long i=1;i<cnt;i++)
num/=10;
int ans = num%10;
printf("%d\n",ans);
}
return 0;
}