题意:给定序列a1a2a3... 定义ai代表1234...i,现在给定位置n,求解出这个位置的数字是多少。
一开始想法简单,妄图利用几个计算就可以判断,
但是后来发现问题,对于序列a12:123456789101112,它是有15位数的,所以不能简单的通过等差数列求和来解决,因为数据量比较大,所以普通方法会超时
那么怎么办呢?这里利用到一个知识, 确定i的位数的方法,i的位数为log10(i)+1
这样,就可以利用动态规划的思想来解决这个题目
len[i]代表1.....i这个子段的长度
sum[i]代表a1a2a3...ai序列的长度
状态转移方程: len[i] = len[i-1] + log10(i) + 1;
sum[i] = sum[i-1] + len[i];
一开始初始化预处理一下,就相当于一个离线算法,然后就可以解决了
其实还有一种更快的想法就是一开始并不用预处理所有的情况,制定一个类似hash的东西,如果没有求出这一项的len[]和sum[],那么在再求解
如果已经有的话就不用求解了,这便实现了动态的离线算法。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define maxn 31270
unsigned int len[maxn]; //len[i]代表1.....i这个子段的长度
unsigned int sum[maxn]; //sum[i]代表a1a2a3...ai序列的长度
unsigned int n;
void init() //初始化一次就可以使用,离线算法
{
len[1] = 1;
sum[1] = 1;
//状态转移方程
//len[i] = len[i-1] + log10(i) + 1;
//sum[i] = sum[i-1] + len[i];
for(int i=2;i<maxn;i++)
{//在序列中,log10(1)+1 求出的是i的位数
len[i] = len[i-1] + (unsigned int) log10((double)i) + 1;
sum[i] = sum[i-1] + len[i];
}
}
int main()
{
int Tcas;
scanf("%d",&Tcas);
init();
while(Tcas--)
{
scanf("%d",&n);
int length = 0,i;
for(i=1;sum[i]<n;i++) ;
int pos = n - sum[i-1];
i = 1;
while(length < pos)
{
length += (int)log10((double)i) + 1;
i++;
}
int ans = ((i - 1) / (int)pow((double)10, length - pos)) % 10;
printf("%d\n", ans);
}
system("pause");
return 0;
}