题目:咕咕东的奇妙序列
题意:
输入:
输出:
样例:
解题思路:这个题还是挺难的,主要是数据太大了,如果用前缀和只能过前六个点,1e18不是闹着玩的,数组装不下的;这里说说我的思路:首先,我先搞个大小为8的数组,存的是从1-10^I的数:
如,数组第一个元素为
112123123412345…123456789大小为45
数组第二个元素为
112123…123…9123…10123…11…123…99到99
第三个元素到999
直到第8个元素(因为第九个元素已经大于1e18了,所以直到第八个元素)
之后,我让给的k减去其中小于k的最大的那个元素;
这时候,k剩下的就是123…(10^I) 123… .(10^i+1)…每个组(即123…x,x之后是1)最后一个元素位数相同;之后用k不停的减去每个组的元素个数,直到k小于那个组的元素个数,但是这里要注意,如果k过大是会超时的;所以我这里采用了二分的方法优化了这个选择的时间;之后剩下的就是一个123…x这样一个组,取其中第k个就行,这里我用的是递归的方法,先用k和9比较,大于9再和180比较(10,11,…99一共180位),大于再继续比较。。。直到小于等于,然后这时候剩下的元素位数都相同(第一个元素一定是10的i次方),你又知道k,求就行了;
代码:
#include<iostream>
#include<cmath>
using namespace std;
long long t[17]={0};
long long h[17]={0};
long long t1[17]={0};
void solve()
{
h[0]=1;
for(int i=1;i<=18;i++)
{
h[i]=h[i-1]*10;
}
t[0]=0;
t1[0]=0;
for(int i=1;i<=16;i++)//t1[i]存的是从1-10^i-1的元素的个数
{
t1[i]=t1[i-1]+i*(h[i]-h[i-1]);
}
for(int i=1;i<=8;i++)//t[i]存的就是从组1到组123...10^1-1的元素的个数
{
t[i]=t[i-1]+(h[i]-h[i-1])*t1[i-1]+i*(h[i]-h[i-1])*(h[i]-h[i-1]+1)/2;
}
}
long long sol1(long long k,int num)
{
long long t=(h[num]-h[num-1])*num;
if(k<=t)//小于等于的情况,这时剩下的元素的位数相同,且第一个元素和k已经知道
{
long long k1=(k-1)/num;//这里就是求第k的数字
long long k2=k%num;
long long n=h[num-1]+k1;
if(k2==0)
{
n=n%10;
return n;
}else
{
long long u=n;
int total=0;
while(u>0)
{
u=u/10;
total++;
}
total=total-k2;
for(int i=0;i<total;i++)
{
n=n/10;
}
n=n%10;
return n;
}
}else//大于前去递归
{
k=k-t;
return sol1(k,num+1);
}
}
long long sol(long long k)
{
long long p;
for(int i=8;i>=0;i--)
{
if(t[i]<=k)
{
k=k-t[i];
p=i;
break;
}
}
if(k==0){return 9;}//减去那个最大的,如果为0直接返回9就行
long long p1=t1[p];//这个是减去的那个从1-10^i-1的元素的个数
p++;
while(1)
{
p1+=p;//这个是1-10^i的元素的个数
long long l=0,r=1000000000;
while(l+1<r)//二分
{
long long mid=(l+r)/2;
long long tt=mid*p1+p*(mid-1)*mid/2;
if(k>tt)
{
l=mid;
}else
{
r=mid;
}
}
k=k-(l*p1+p*(l-1)*l/2);//减去,之后剩下的就是123....x
k=sol1(k,1);//判断
return k;
}
}
int main()
{
solve();
int q;
cin>>q;
while(q--)
{
long long k;
cin>>k;
long long h1=sol(k);
cout<<h1<<endl;
}
}