本题不是求不大于 n 的Nya数的个数,而是求给定区间内第 K 大的Nya数。有两种做法:一种是二分+模板,还有一种一直接构造。下面的代码是直接构造。
关于 f :起初定义为最高位不大于 l 位(个位是 0 位)的且含 i 个4,j 个7的且大于 P 的数的个数。后发现这样定义根本就是错的。因为若f的值与P挂钩,那么在递归过程中就会受到P的各种限制,丧失了 f 记忆化的优越性。因此,f的定义应该与 P 和 Q 无关,而仅与 Nya数的性质有关,所以应该常规化:f[l][i][j]表示最高位不大于l 位的且含 i 个4,j 个7的数的个数。
dfs()由高位到低位产生区间[P+1,Q]内第 k 个Nya数。每一位的约束条件是:截止到当前位置,其下界不小于P,上界不大于Q。一个全局变量 kth 表示当前在[P+1,Q]上已经找到的 Nya数的个数,当kth == k,表示已经构造完毕.假设当前最高位 mm 为 s,那么dfs(mm-1)求出在最高位是 s 的前提下,满足条件的 Nya 数的个数。
若个数小于k,那么最高位变为 s+1,继续dfs(mm-1);若个数大于等于k,构造完毕。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define clr(A) memset(A,0,sizeof(A))
#define mod 1000000007
using namespace std;
typedef __int64 LL;
LL P,Q,kth = 0;
int x,y;
int numP[40],numQ[40],ans[40];
LL f[40][30][30];
int open(LL x,int a[])
{
int res = 0;
for(; x; x /= 10) a[res++] = x % 10;
return res;
}
int dfs(int l,int e1,int e2,int x1,int x2,LL k)
{
if(l == -1)
{
if(x1 == 0 && x2 == 0 ) { kth++; return (kth == k);}
return 0;
}
if(x1 < 0 || x2 < 0) return 0;
if(!e2 && !e1 && f[l][x1][x2] != -1 && kth+f[l][x1][x2] < k) {
kth += f[l][x1][x2];
return 0;
}
LL tmp = kth;
int low = e1?numP[l]:0, up = e2?numQ[l]:9;
for(int d = low; d <= up;d++)
{
ans[l] = d;
if(dfs(l-1,e1&&d==numP[l],e2&&d==numQ[l],x1-(d==4),x2-(d==7),k)) return 1;
}
if(!e2&&!e1) f[l][x1][x2] = kth-tmp;
return 0;
}
void print(int l)
{
while(ans[l] == 0) l--;
for(; l>=0;l--) printf("%d",ans[l]);
printf("\n");
}
int main()
{
int T,N,c = 0;
LL k;
scanf("%d",&T);
memset(f,-1,sizeof(f));
while(T--)
{
clr(ans),clr(numP),clr(numQ);
printf("Case #%d:\n",++c);
scanf("%I64d%I64d%d%d%d",&P,&Q,&x,&y,&N);
int mp = open(P+1,numP),mq = open(Q,numQ);
for(int i = 0; i < N; i++)
{
scanf("%I64d\n",&k);
kth = 0;
if(dfs(mq-1,1,1,x,y,k)) print(mq);
else printf("Nya!\n");
}
}
return 0;
}