数位dp_HDU_3943_K-th Nya Number

本文介绍了一种高效算法,用于寻找指定区间内的第K大Nya数。通过直接构造方法,结合动态规划思想预处理计数函数f,实现从高位到低位递归产生目标数值。该算法巧妙地解决了计数函数f与上下界限制冲突的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    本题不是求不大于 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值