http://codeforces.com/contest/1216/problem/E2
这种找位置的题我还挺不熟练的,不过今天一发就过了,第一次ak div3,开心
先考虑1 12 123 1234这种段,我们先确定在哪一段里面,
我们可以发现,这些段的长度是1,2,3.....9,11,13,....这样的,也就是最后一位变成2位数后,每多一个数+2,3位数就每多一个数+3以此类推。
这样,我们记录一下sum,求出末尾位数为i的段的总长度,然后做前缀和,我们就可以定位到k在末位数字位数的哪一段里,然后k减去前面的sum前缀和。
接着,由于当前末位数字为id位,所以每一段的长度是等差数列,可以用二分定位到是哪一段,也就是末位数字的具体数值是多少,并k减去之前的
接下来我们再考虑在这一段123......len中,第k个在哪里,那么前9个是1,后面是一段2,再后面是一段3。。。和之前一样,定位到到底在哪个数字中,再定位到这个数字中的第几位数就行了
#include<bits/stdc++.h>
#define maxl 300010
using namespace std;
int n,m,ans,up,top;
unsigned long long k;
unsigned long long st[201],ed[201],sum[201],num[201];
int s[maxl];
inline void init()
{
unsigned long long mi=1;
st[0]=0;ed[0]=0;
for(int i=1;i<=64;i++)
{
st[i]=ed[i-1]+i;
num[i]=((mi*10-1)-mi+1);
ed[i]=st[i]+i*(num[i]-1);
sum[i]=sum[i-1]+(st[i]+ed[i])*num[i]/2;
up=i;
if(sum[i]>=1e18)
break;
mi*=10;
}
}
inline void prework()
{
scanf("%lld",&k);
}
inline int findans(unsigned long long x,int pos)
{
top=0;
while(x>0)
{
s[++top]=x%10;
x/=10;
}
return s[top-pos+1];
}
inline void mainwork()
{
unsigned long long mi=1,id;
for(int i=1;i<=up;i++)
{
if(sum[i]>=k)
{
id=i;
break;
}
mi*=10;
}
k-=sum[id-1];
unsigned long long l=0,r=num[id]-1,mid;
unsigned long long t,d;
while(l+1<r)
{
mid=(l+r)>>1;
t=(st[id]+id*(mid-1)+st[id])*mid/2;
if(t<k)
l=mid;
else
r=mid;
}
if((st[id]+id*(r-1)+st[id])*r/2<k)
d=r;
else
d=r-1;
k-=(st[id]+id*(d-1)+st[id])*d/2;
unsigned long long len=st[id]+d,sum=0;
for(int i=1;i<=up;i++)
{
if(k<=num[i]*i)
{
id=i;
break;
}
k-=num[i]*i;sum+=num[i];
}
unsigned long long val=k/id;
if(k%id==0)
ans=findans(val+sum,id);
else
ans=findans(val+sum+1,k%id);
}
inline void print()
{
printf("%d\n",ans);
}
int main()
{
init();
int t=1;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}