题目
时间限制
1s
空间限制
64MB
题目描述
咕咕东 正在上可怕的复变函数,但对于稳拿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<=1018)
输出格式
输出包含q行
第i行输出对询问ki的输出结果。
样例输入
5
1
3
20
38
56
样例输出
1
2
5
2
0
数据点
做法
1、sum[x]表示数列中第一次出现x的位置,a[x]表示从1~x所占位数。
2、对于每个输入的位置k,用二分法找第一个sum[mid]>=k的mid,mid即为k所在的前项和。
3、令k=k-weizhi(mid-1,0),求出第一个a[mid]>k的mid即为k所在的数字。
4、令k=k-weizhi(mid-1,1),k为所在数字的级数,将该数字mid转化为字符串,求出第k位即为所求的输出数字。
5、在求位置的函数中,将求sum和求a合在一起进行,因为求sum需要a作为上一级的数列长度来求解。
6、先不断求出到pow数量级的sum和a,当x在该数量级中,再求出从该数量级最小值到x的sum和a
错误
1、注意在两次查找时,均找的是第一个使返回结果>k的值,所以当weizhi(mid,0/1)>=k时,应使mid=r-1而非r+1
2、一开始考虑将前项和全部求出储存在数组中,发现会出现RE的错误,考虑应该是数组越界的原因,因为数据范围为1e18,所以不考虑将其存到一个大数组中(但是感觉应该可以存到按数量级递增的一个数组中)。
3、to_string 是c++11支持的,在低版本中不支持
代码
#include<iostream>
#include<string>
#include<cmath>
#include<cstdio>
using namespace std;
long long weizhi(long long x,int ch)
{
long long pow=1,sum=0,a=0,n=0,weishu=0;
while(true)
{
pow=pow*10;
weishu=weishu+1;
if(x>pow-1)
{
n=pow-pow/10;
sum=sum+a*n+(n+1)*n/2*weishu;
a=a+n*weishu;
}
else
{
n=x-pow/10+1;
sum=sum+a*n+(n+1)*n/2*weishu;
a=a+n*weishu;
break;
}
}
if(ch==0)
return sum;
else
return a;
}
int main()
{
long long ans,k;
long long q,l,r,mid;
cin>>q;
for(int i=0;i<q;i++)
{
cin>>k;
l=0;r=1e9;
while(l<=r)
{
mid=(l+r)>>1;
if(weizhi(mid,0)>=k)
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
k=k-weizhi(ans-1,0);
l=0;
r=ans+1;
while(l<=r)
{
mid=(l+r)>>1;
if(weizhi(mid,1)>=k)
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
k=k-weizhi(ans-1,1);
string s =to_string(ans);
printf("%d\n",s[k-1]-'0');
}
return 0;
}