题解 P1895 【数字序列】

Forever_Pursuit

2019-10-28 14:40:22

Solution

$\text{upd on 2020.2.28:}$ 增添了一些说明 题目描述 在下列的无穷数字序列$1121231234......$ 中,查找第 $n$ 个数字。 对于任意一个子数字序列 $1234...i$ ,我们可以先预处理出它的长度,记为$len_i$。 预处理部分代码(十分暴力,应该很好理解): ```cpp len[0]=0; for(register int i=1;i<10;i++) len[i]=len[i-1]+1; for(register int i=10;i<100;i++) len[i]=len[i-1]+2; for(register int i=100;i<1000;i++) len[i]=len[i-1]+3; for(register int i=1000;i<10000;i++) len[i]=len[i-1]+4; for(register int i=10000;i<100000;i++)//预处理大一点 len[i]=len[i-1]+5; ``` 对于题目所求的序列的第 $n$ 个数字,要先求出它在原序列哪个子序列中。 ```cpp scanf("%d",&n); int s=0,k1=0,t=n; while(++k1){ s+=len[k1]; if(s>=t)break;//保证s>=t,s-len[k1]<t } t-=(s-len[k1]);//s-len[k1]即为前(k1-1)个子序列的总长度 ``` 这样,我们就知道了答案就是第 $k_1$ 个子序列中的第 $t$ 个数。 显然对于任意一个 $i$ $(i∈N^+)$ ,第 $i$个子序列是第 $i+1$ 个子序列的子序列。 于是我们继续枚举: ```cpp int k2=0; while(++k2) if(len[k2]>=t)break;//使得len[k2]>=t,len[k2-1]<t ``` 即找到最小的 $k_2$ ,使第 $k_2$ 个子序列是至少有 $t$ 位的。 答案就变成了第 $k_2$ 个子序列中的倒数第 $(len_{k_2}-t+1)$ 个数,也就是 $k_2$ 这个数的倒数第 $(len_{k_2}-t+1)$ 位数。 如过要求一个数 $x$ 的倒数第 $y$ 位,就是求 $x/10^{y-1}$ 对 $10$ 取模。 所以 $k_2$ 的倒数第 $(len_{k_2}-t+1)$ 位就是 $k_2/10^{len_{k_2}-t}$ 对 $10$ 取模。 然后把这个数求出来就行了。 ```cpp printf("%d\n",(int)(k2/pow(10,len[k2]-t))%10); ``` 完整代码: ``` #include<bits/stdc++.h> using namespace std; #define rint register int #define N 100001 int t,n,len[N]; int main(){ for(rint i=1;i<10;i++) len[i]=len[i-1]+1; for(rint i=10;i<100;i++) len[i]=len[i-1]+2; for(rint i=100;i<1000;i++) len[i]=len[i-1]+3; for(rint i=1000;i<10000;i++) len[i]=len[i-1]+4; for(rint i=10000;i<100000;i++) len[i]=len[i-1]+5; scanf("%d",&t); while(t--){ scanf("%d",&n); int s=0,k1=0,k2=0; while(++k1){ s+=len[k1]; if(s>=n)break; } n-=(s-len[k1]); while(++k2) if(len[k2]>=n)break; printf("%d\n",(int)(k2/pow(10,len[k2]-n))%10); } return 0; } ```