UVA_10706
首先我们可以把这串数中的通项摘出来,就是12345……(n-1)n,这串数就是由这个通项依次拼接而成的。
意识到上面一点后,我们不妨把原序列的第i位转化成是哪一通项中的哪一位,这样我们不就好处理多了么。要能够实现这样的转化,我们需要知道两组数据,第一,每个通项中有多少个数字,第二,根据第一组数据算出从第一项开始至第n项一共有多数字。这样我们就可以算出第i位是在第n+1项中的i-sum[n]项,其中sum[]代表的就是第二组数据。在算这两组数据的时候我们采用递推的方式去计算,而且我们只要把最大的情况算出来并保留中间结果即可(也即打表),这样就避免了重复计算。
下面的问题就是最大情况是什么样的情况了,我们不妨用高斯公式算一下,就算通项每项只增加一个数字,那么最大项的标号也不会超过sqrt(imax)+1,何况有时还不只增加一个数字。
但是由于实际上的最大项的长度要大一些,所以我们打表的时候也要把数组开大一些。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#define MAX 2147483647
int K;
long long int sum[100000];
char b[1000000];
int pan(int x)
{
int n=0;
while(x)
{
x/=10;
n++;
}
return n;
}
void init()
{
int i,j,a;
K=sqrt(MAX)+1;
j=1;
for(i=1;i<=K;i++)
{
sprintf(&b[j],"%d",i);
while(isdigit(b[j]))
j++;
}
a=sum[0]=0;
for(i=1;i<=K;i++)
{
a+=pan(i);
sum[i]=sum[i-1];
sum[i]+=a;
}
}
int main()
{
int i,j,k,n,num,t,min,mid,max;
init();
scanf("%d",&t);
while(t--)
{
scanf("%d",&num);
min=0;
max=K;
while(1)
{
mid=(min+max)/2;
if(mid==min)
break;
if(sum[mid]>=num)
max=mid;
else
min=mid;
}
k=num-sum[mid];
printf("%c\n",b[k]);
}
return 0;
}