题目描述
有一个无限序列,它由若干个部分组成,其中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数字。求第k项数字是多少?(一位数字算一项)
Input
输入由多行组成。
第一行一个整数q表示有q组询问
接下来第i+1行表示第i个输入ki,表示询问第ki项数字
Output
输出包含q行
第i行输出对询问ki的输出结果。
解题思路
首先来看这个序列,将这个序列整理成这种形式:
1
1 2
1 2 3
……
1 2 3 …… 9
1 2 3 …… 9 1 0
……
其中每一行就是题目说的每一部分,而每一行的数字个数是有规律的,对于第n行,设s[n]是该行的项数
- 如果n是1位数,则s[n]=s[1]+(n-1);
- 如果n是2位数,则s[n]=s[10]+2*(n-10);
- 如果n是k位数,则s[n]=s[10(k-1)]+k*(n-10(k-1)];
其中s[1],s[10]等的关系则满足:
- s[1]=1;
- s[10]=s[9]+2;
- ……
- s[10(k-1)]=s[10(k-1)-1]+k;
由此可以发现,这个序列每一行的项数,以每一行行序号的位数公差,构成了不同的等差数列,因此求项数就可以转换为多个等差数列求和,反之,当我们知道某个项时,我们可以利用二分的方法来反推出该项在那一部分(哪一行),到此,完成了第一步。
然后我们观察对于某一行的数字,它也可以分为不同部分:
- 前9个数,每个数就是一项;
- 第10到第99个数,每个数占两项;
也就是说根据数的位数不同,依然可以将每一行的数划分为不同部分,k位数属于第k部分,所有第k部分的数字,每个数字占k项,每个部分的项数,依然是以k为公差的等差数列,也即是说,对于给定的一行,我可以求得某个数字在该行的第几项,反过来,如果我知道某个数字在第几项,那我也可以利用二分反推出这个数字是几,到此,完成了第二步;
然后对于某一个数字,它是几位数,就占几项,如果我们知道一个数在这个数的第几项,那我们就能反推出这个数是几(求模运算),至此,所有的任务就完成了,整个程序的过程可以分为以下几步:
- 对于给定的第k项,二分求解出第k项在第几个部分(第几行),假设为第n行;
- 此时可以解得第k项在第n行的第几项,假设为第i项;
- 对于第n行,可以再次二分求解出,第i项对应哪个数字,假设为p;
- 此时可以解得第k项在数字p的哪一位,然后利用取模运算,找到这一位的数字输出即可。
实现代码
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
long long ans[20];
long long find_num(long long k)
{
long long d=1,a=1,ans=0,num=1,n;
while(k>=10*num)
{
num*=10;
n=num-num/10;
ans+=a*n+n*(n-1)*d/2;
d++;
a+=(n-1)*(d-1)+d;
}
n=k-num+1;
ans+=a*n+n*(n-1)*d/2;
return ans;
}
long long find_num2(long long k)
{
long long cnt=1,a=0,n=1;
while(k>=10*n)
{
a+=9*n*cnt;
cnt++;
n*=10;
}
a+=cnt*(k-n+1);
return a;
}
int main()
{
int q;
scanf("%d",&q);
//cin>>q;
for(int i=0;i<q;++i)
{
long long k;
scanf("%lld",&k);
long long l=0,r=1e9,n,mid;
long long s;
while(l<=r)
{
mid=(l+r)/2;
s=find_num(mid);
if(s<k)
{
n=mid;
l=mid+1;
}
else
r=mid-1;
}
k-=find_num(n);
l=0;
r=n+1;
while(l<=r)
{
mid=(l+r)/2;
s=find_num2(mid);
if(s<k)
{
n=mid;
l=mid+1;
}
else
r=mid-1;
}
k-=find_num2(n);
n++;
int j=0;
while(n>0)
{
ans[j]=n%10;
j++;
n/=10;
}
printf("%d\n",ans[j-k]);
memset(ans,0,sizeof(ans));
}
return 0;
}
总结
这道题看似是一道二分的题目,其实是一道数学题,一道找规律的题目,只要能找到第n行的项数与n的位数的关系,就很好解决了。
另外,这道题数据卡的很极限,所以不能用pow,虽然我在本地测试的数据,都对了(构造的各种极限点都没问题),但评测机还是WA了,因为会有精度丢失,这是我之前怎么都没想到的,奇怪的debug知识增加了!(深夜连WA13发)