Apocalypse Someday

POJ

定义魔鬼的数是数字的十进制表示中至少有三个连续的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("\n");
	}
	return 0;
}

posted on 2019-07-28 10:37  PPXppx  阅读(127)  评论(0编辑  收藏  举报