POJ 3208 Apocalypse Someday

H y p e r l i n k Hyperlink Hyperlink

http://poj.org/problem?id=3208


D e s c r i p t i o n Description Description

定义包含三个连续的6的数为魔鬼数
求第 n n n小的魔鬼数

数据范围: n ≤ 5 × 1 0 9 n\leq 5\times 10^9 n5×109


S o l u t i o n Solution Solution

f [ i ] [ j ] f[i][j] f[i][j]表示长度为 i i i,前 j j j位都是6的魔鬼数个数(当 j > 3 j>3 j>3时,默认存到 j = 3 j=3 j=3处),该 d p dp dp允许前导0的存在

对于 f [ i ] [ 0 ] f[i][0] f[i][0],只要这一位填的不是6,都可以转移过来,即
f [ i ] [ 0 ] = 9 × ( f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] + f [ i − 1 ] [ 2 ] ) f[i][0]=9\times (f[i-1][0]+f[i-1][1]+f[i-1][2]) f[i][0]=9×(f[i1][0]+f[i1][1]+f[i1][2])

对于 f [ i ] [ 1 ] f[i][1] f[i][1],这一位只能填6,即
f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] f[i][1]=f[i-1][0] f[i][1]=f[i1][0]

显然 f [ i ] [ 2 ] = f [ i − 1 ] [ 1 ] f[i][2]=f[i-1][1] f[i][2]=f[i1][1] f [ i ] [ 3 ] = f [ i − 1 ] [ 2 ] f[i][3]=f[i-1][2] f[i][3]=f[i1][2]
但是,如果上一位前已经满足是魔鬼数,则这一位可以随便填,即
f [ i ] [ 3 ] + = f [ i − 1 ] [ 3 ] × 10 f[i][3]+=f[i-1][3]\times 10 f[i][3]+=f[i1][3]×10

接下来用试填法或二分答案转判定都可


C o d e Code Code
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;int T,k;
LL n,f[25][4],m,cnt;
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
signed main()
{
	f[0][0]=1;
	for(register int i=0;i<21;i++)
	{
		for(register int j=0;j<3;j++)
		{
			f[i+1][j+1]+=f[i][j];
			f[i+1][0]+=f[i][j]*9;
		}
		f[i+1][3]+=f[i][3]*10;
	}
	T=read();
	while(T--)
	{
		n=read();m=3;
		while(f[m][3]<n) m++;k=0;//计算答案的位数,k表示目前已经填了几个连续的6
		for(register int i=m;i;i--)
		{
			for(register int j=0;j<=9;j++)//考虑j填0~9,第i位的填法
			{
				cnt=f[i-1][3];
				if(j==6||k==3)
				//如果这一位填的是6,如果是第一个,则后面要加上f[i-1][2]
				//如果是第二个6,则后面还要加上f[i-1][1]
				//如果是第三个6,则后面还要加上f[i-1][0]
				 for(register int l=max(3-k-(j==6),0);l<3;l++)
				  cnt+=f[i-1][l];
				if(cnt<n) n-=cnt;
				else
				{
					putchar(j+48);
					if(k<3) 
					{
						if(j==6) k++;
						else k=0;
					}
					break;
				}
			}
		}
		putchar(10);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值