定义魔鬼的数是数字的十进制表示中至少有三个连续的6,如666,1666,6663,16666,6660666...求第(n(n<=5*10^7))小的魔鬼的数.
分析:设(f[i][3])表示由i位数字构成的魔鬼树有多少个,设(f[i][j](0<=j<=2))表示由i位数字构成的、开头已经有连续j个6的非魔鬼数有多少个.(允许前导0)
(f[i][0]=9*(f[i-1][0]+f[i-1][1]+f[i-1][2]))
(f[i][1]=f[i-1][0],f[i][2]=f[i-1][1])
(f[i][3]=f[i-1][2]+10*f[i-1][3])
我们可以先预处理(f)数组.然后对于每个询问n,我们先求出第n小魔鬼数的位数,然后从小到大枚举每一位数字是多少,即可得到这种填法可以得到的魔鬼数的数量,然后与n比较.如果比n小,说明还应该这一位的数字还应该更大,否则当前位就是枚举的该数.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
ll f[21][4];
inline void prework(){
f[0][0]=1;
for(int i=1;i<=20;++i){
f[i][0]=9*(f[i-1][0]+f[i-1][1]+f[i-1][2]);
f[i][1]=f[i-1][0];
f[i][2]=f[i-1][1];
f[i][3]=f[i-1][2]+10*f[i-1][3];
}
}
int main() {
prework();
int T=read();
while(T--){
int n=read(),m;
for(m=3;f[m][3]<n;++m);
for(int i=m,k=0;i;--i){
for(int j=0;j<=9;++j){
ll cnt=f[i-1][3];
if(j==6||k==3)
for(int l=max(3-k-(j==6),0);l<3;++l)cnt+=f[i-1][l];
if(cnt<n)n-=cnt;
else{
if(k<3){
if(j==6)++k;
else k=0;
}
printf("%d",j);
break;
}
}
}
printf("
");
}
return 0;
}