题意:
一个奇怪的无限序列:112123123412345…这个序列由连续正整数组成的若干部分构成,其中第一部分包含1至1之间的所有数字,第i部分包含1至i之间的所有数字。注意12345678910的10为两项。现在要求出第k项数字是多少。
思路:
先看每一部分的长度:1 2 3 4 5 6 7 8 9 11 13 15…能看出这由多个等差数列组成,我们就可以用等差数列前n项和公式求出某一部分在序列中的位置。利用二分可以得出第ans部分里有第k项数字。然后看第ans部分:1 2 3 4 5 6 7 8 9 11 13…(这是每个数对应的位置,如10对应位置11),这也是等差数列,且与第一部分的数列相同。我们就可以求出第ans个数字里有第k项数字,且能求出数字在ans中的位置,这样就得到了答案。
func函数:当flag为1时,计算某一部分在序列中的位置,即在总序列中112123…x共多长。用等差数列求和公式来算,求项数公差首项末项的公式可以看代码。最终得到的sum就是某一部分在序列中的位置。当flag为2时,应计算第ans部分123…ans中的数字x的位置,即在123…ans中123…x有多长。可以发现这次要求的是数列的最后一项an。
然后是二分:一开始求第k项在第几部分里,l=0,r=1e9,逐渐逼近,求出ans。k=k-func(ans-1,1)后,答案就是第ans部分123…ans中的第k位,继续用二分算,l=0,r=ans+1,逐渐逼近,求出ans。此时答案就在数字ans中,且为第k位,k=k-func(ans-1,2)。最后输出ans的第k位即可。
总结:
一道二分题目,对应CSP T4。题目很难,做题时只暴力输出了前56项的数据,拿到30分。补题时也写了很久。不用二分一点点求第k项会T,用pow(10,ws)函数会WA,应该是返回值double转换为long long int出现错误了。这道题能想到二分也很不容易。
代码:
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
//用pow(10,ws)为WA,应该是返回值double转换为long long int时出现错误
long long int k;
int z[57]= {0,1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1,2,3,4,5,6,1,2,3,4,5,6,7,
1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,0};
//flag=1时,在总序列中返回112123......x共多长
//flag=2时,在一个数字生成的序列123...ans中返回123..x共多长
long long int func(long long int x,int flag)
{
long long int sum=0,ws=0; //记录数字当前位数,如10表示为两位数10~99
//等差数列求前n项和,其中每一项都是由一个数字生成的序列长度
long long int n=0,a1=0,an=0,d=0;
while(1)
{
if(ws==0) ws=1;
else ws=ws*10;
if(x>ws*10-1)
{
n=10*ws-ws; //项数
d++; //公差
a1=an+d; //首项,为上一次的末项再加上这次的公差
an=a1+(n-1)*d; //末项
sum=sum+(a1+an)*n/2;
}
else //x与ws同一位数
{
n=x-ws+1;
d++;
a1=an+d;
an=a1+(n-1)*d;
sum=sum+(a1+an)*n/2;
break;
}
}
if(flag==1) return sum;
else return an;
}
int main()
{
int q;
cin>>q;
for(int zs=0; zs<q; zs++)
{
cin>>k;
if(k<=56)
cout<<z[k]<<endl;
else
{
long long int l=0,r=1e9,mid,ans=0;
while(l<=r)
{
mid=(l+r)/2;
if(func(mid,1)>=k)
ans=mid,r=mid-1;
else l=mid+1;
}
//此时答案在112123.....123...ans中
k=k-func(ans-1,1); //答案为123...ans中的第k位
l=0,r=ans+1;
while(l<=r)
{
mid=(l+r)/2;
if(func(mid,2)>=k)
ans=mid,r=mid-1;
else l=mid+1;
}
//此时答案在数字ans中
k=k-func(ans-1,2); //答案锁定为数字ans中的第k位
string theAns=to_string(ans);
cout<<theAns[k-1]<<endl;
}
}
}