CodeForces - 747F - Igor and Interesting Numbers dp+暴力

题目链接

题意:从所有数字出现次数均不超过 t t t 次的16进制数中找到第 k k k 小个数是多少。

思路:显然,由于 k k k 的最大不超过2e9 ,所以这个答案的位数不会超过9位,那么我们可以先枚举每个位数下满足题目要求的十六进制数的个数,找到答案的位数,然后从高位到低位枚举即可。
对于每种长度的数目个数可以暴力求得,设一个数组 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 前 i i i 个数使用了 j j j 个位置的方法数。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define PI acos(-1)
#define INF 0x3f3f3f3f
#define NUM 200010
#define debug true
#define lowbit(x) ((-x) & x)
#define ffor(i, d, u) for (int i = (d); i <= (u); ++i)
#define _ffor(i, u, d) for (int i = (u); i >= (d); --i)
#define mst(array, Num, Kind, Count) memset(array, Num, sizeof(Kind) * (Count))
const int P = 1e9 + 7;
template <typename T>
void read(T &x)
{
    x = 0;char c;T t = 1;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-'){t = -1;c = getchar();}
    do(x *= 10) += (c - '0');while ((c = getchar()) >= '0' && c <= '9');
    x *= t;
}
template <typename T>
void write(T x)
{
    int len = 0;char c[21];
    if (x < 0)putchar('-'), x *= (-1);
    do{++len;c[len] = (x % 10) + '0';} while (x /= 10);
    _ffor(i, len, 1) putchar(c[i]);
}
int t;
ll k, c[15][15];
ll dp[16][20];
int limit[16];
char num[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
ll check(const int &len)
{
    memset(dp, 0, sizeof(dp));
    ffor(i, 0, min(limit[0], len)) dp[0][i] = c[len][i];
    ffor(i, 1, 15)
        ffor(j, 0, len)
            ffor(z, 0, min(limit[i], j))
                dp[i][j] += (dp[i - 1][j - z] * c[len - j + z][z]);
    return dp[15][len];
}
void AC()
{
    read(k), read(t);
    c[0][0] = c[1][0] = c[1][1] = 1;
    ffor(i, 2, 14)
    {
        c[i][0] = c[i][i] = 1;
        ffor(j, 1, i - 1)
            c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
    }
    ffor(i, 0, 15) limit[i] = t;
    int len = 1;
    ll ans;
    for (;; ++len)
    {
        ans = 0;
        if(len == 1)
            ans = 15;
        else
            ffor(i, 1, 15)
            {
                --limit[i], ans += check(len - 1);
                ++limit[i];
            }
        if (ans < k)
            k -= ans;
        else
            break;
    }
    _ffor(i, len, 1)
    {
        ffor(j, 0, 15)
        {
            if (i == len && j == 0)
                continue;
            if (limit[j] == 0)
                continue;
            --limit[j];
            if ((ans = check(i - 1)) >= k)
            {
                putchar(num[j]);
                break;
            }
            k -= ans, ++limit[j];
        }
    }
}
int main()
{
    AC();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值