题目描述
咕咕东 正在上可怕的复变函数,但对于稳拿A Plus的 咕咕东 来说,她早已不再听课,此时她在睡梦中
突然想到了一个奇怪的无限序列:112123123412345 …这个序列由连续正整数组成的若干部分构成,其
中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所
有数字,第i部分总是包含1至i之间的所有数字。所以,这个序列的前56项会是
11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是
5,第38项是2,第56项是0。咕咕东 现在想知道第 k 项数字是多少!但是她睡醒之后发现老师讲的东西
已经听不懂了,因此她把这个任务交给了你。
输入格式
输入有多行组成。
第一行一个整数 q 表示有 q 组询问(1 <= q <= 500)
接下来第 i+1 行表示第 i 个输入 ki,表示询问第 ki 项数字。(1 <= ki <= 10^18)
输出格式
输出包含 q 行。
第 i 行输出对询问 ki 的输出结果。
样例输入
5
1
3
20
38
56
样例输出
1
2
5
2
0
问题分析
因为 k 是特别大的,不能采取暴力的枚举每一位,这样子很明显会超时。我们看一下序列的规律,容易发现:我们可以先进行分组,第i组即为题干中的第i部分总是包含1至i之间的所有数字。1-9组每个组的项数是等差增加的,d=1;10到99组每个组的项数是等差增加的,d=2;…等差数列求和有公式,利用公式可以求得项数和。利用二分法,求出k所在的组数,再在相应的组数里求出项值。每一组的项一样是等差数列,找到规律求解。
注意要采用数据类型long long,一些乘法也要注意,否则可能会wrong answer。
#include<stdio.h>
long long solve(long long n){
long long sum=0,a=1,s=9,d=1;
while(n){
if(n>s){
sum+=a*s+s*(s-1)*d/2;
n-=s;
}
else{
sum+=a*n+n*(n-1)*d/2;
return sum;
}
a+=s*d+1;s*=10;d++;
}
}
int main(){
long long k;
int q;
scanf("%d",&q);
for(int i=0;i<q;i++){
scanf("%lld",&k);
long long l=1,r=1e9,ans=0;
while(l<=r){
long long mid=(l+r)>>1;
long long t=solve(mid);
if(t<=k){
ans=mid;
l=mid+1;
}
else r=mid-1;
}
k=k-solve(ans);
long long n=9,ans1=0,d=1;
while(k){
if(k>n*d){
ans1+=n;
k-=n*d;
}
else{
ans1+=k/d;
k=k%d;
break;
}
d++;n*=10;
}
if(ans1==0)ans1=ans;
if(k==0) printf("%lld\n",ans1%10);
else{
ans1+=1;
while(d!=k){
d--;
ans1/=10;
}
printf("%lld\n",ans1%10);
}
}
}