题意
咕咕东 正在上可怕的复变函数,但对于稳拿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组询问
接下来第i+1行表示第i个输入 ,表示询问第ki项数字(1<=ki<=10^18)
输出
输出包含q行 第i行输出对询问 的输出结果。
思路
首先要清楚的是求的第k位数,9之后的数不止以为;
初始想的是用数组n[i] c[i] s[i]分别表示第i个数的位数、第i部分的位数、截至到第i部分的位数,这样通过二分查找
s[i]中使得s[i]<=k的最大的i设为a1,然后令t=k-s[a1]继续在c[i]中进行二分查找使得c[i]<=t的最大的i,然后确定所在n[i],求的在哪一位即可。但是由于数据范围为1e8,所以显然数组是不可以的,所以这里将s[i] c[i] n[i]变成函数形式,用函数来求的一个i的s,c,n,求取技巧:(找规律)
由于 [10^i, 10^(i+1)) 的数长度均为 i +1 ,所以这里 每个 [10^i, 10^(i+1)) 里的每个c(i) 就是一个等差数列,差为 i +1 ;
所以给定x, s(x) = ans[i] + (i+1)∗∑(1,x-i+1) ; ( 10^i 为<=x的最大的10的次方数);
ans(i) = (i+1) ∗ ∑(1,区间长度的和)+ ( c(10^i - 1) - c(10^(i-1) - 1)) ∗ (x−10^i + 1) + ans(i-1);
总结
这道题是一道先进行理解分块,然后运用二分查找一层一层的确定位置
反思
当数据范围大时,学会用函数代替数据求值
代码
#include<iostream>
using namespace std;
long long getn(long long x)
{ long long count=0;
while(x)
{
x=x/10;count++;
}return count;
}
long long gets(long long x)
{
long long res=0,i=1,j=1;
for(;j*10<=x;i++,j=j*10)
{
long long t=(j*9)*(j*9+1)/2;
res=res+i*t+i*j*9*(x-j*10+1);
}
long long w=(x-j+1)*(x-j+2)/2;
res=res+i*w;return res;
}
long long getc(long long x)
{
long long res=0,i=1,j=1;
for(;j*10<=x;i++,j=j*10)
res=res+i*j*9;
res=res+i*(x-j+1);
return res;
}
long long binarysearch(bool flag,long long l,long long r,long long x)//flag=0 gets
{
int ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(!flag)
{
if(gets(mid)<=x)//找出左边第一个小于 小于等于k的
{
ans=mid;l=mid+1;
}
else r=mid-1;
}
else
{
if(getc(mid)<=x)
{
ans=mid;l=mid+1;
}
else r=mid-1;
}
} return ans;
}
int main()
{
int q;cin>>q;
while(q--)
{
long long k;cin>>k;
long long a1=binarysearch(0,1,1e9,k);
if(gets(a1)==k)
{
int ans=a1%10;cout<<ans<<endl;continue;
}
else
{ long long t=k-gets(a1);
long long a2=binarysearch(1,1,a1+1,t);
if(getc(a2)==t)
{
int ans=a2%10;cout<<ans<<endl;continue;
}
else
{
long long a3=getn(a2+1)-t+getc(a2)+1;
long long sum=0;//a2
long long a4=a2+1;int ans;
while(a4)
{ ans=a4%10;
a4=a4/10;
sum++;
if(sum==a3)break;
}cout<<ans<<endl;
}
}
} return 0;
}