51Nod-1635-第K个幸运排列

来源:互联网 发布:js设置p标签的值 编辑:程序博客网 时间:2024/05/09 10:56

ACM模版

描述

描述

题解

1n 的排列数是 n!,阶乘的增长速度是恐怖的,题目中 1n,k109,那么 n 只要超过 1313!=6,227,020,800,已经超过了 k 的最大范围,所以我们很容易想到的是,将 1n 序列分成两部分,前部分是 1ncnt,后部分是 ncnt+1n,至于 cnt 嘛,当然是满足 cnt!k 的最小值。

分成如上两部分后,前部分肯定是顺序排列,所以我们可以利用数位 dp 来进行处理一下,只需要考虑有多少数字只包含 47 就好了,因为下标和数字是一样的。至于后部分,有一个叫做康拓展开式的东西可以派上用场,以前做八数码问题时好像用过这个来标记状态,这个东西可以很容易的进行状态的标记与还原,这里需要用到还原部分,也就是对 k 进行逆康拓展开即可,然后统一加上基数 ncnt,因为后边这个部分的数很少很少,不超过 13 个,所以呢,直接暴力枚举判断即可。

涨姿势了……第一次使用逆康托展开,很强势。

代码

#include <iostream>using namespace std;typedef long long ll;const int MAXN = 15;const int MAX_FAC = 14;const int MAX_POW = 33;const int MAGIC_4 = 4;const int MAGIC_7 = 7;ll n, k;int num[MAXN];ll pow[MAX_POW];ll fac[MAX_FAC];//  数位 DP,求 1~x 有多少幸运数字ll dp(ll x){    if (x == 0)    {        return 0;    }    int pos = 0;    while (x)    {        num[++pos] = x % 10;        x /= 10;    }    ll res = 0;    for (int i = pos; i >= 1; i--)    {        if (num[i] > MAGIC_7)        {            res += pow[i];            break;        }        else if (num[i] == MAGIC_7)        {            res += pow[i - 1];            if (i == 1)            {                res++;            }        }        else if (num[i] > MAGIC_4)        {            res += pow[i - 1];            break;        }        else if (num[i] == MAGIC_4)        {            if (i == 1)            {                res++;            }        }        else if (num[i] < MAGIC_4)        {            break;        }    }    for (int i = 1; i < pos; i++)    {        res += pow[i];    }    return res;}bool vis[MAXN];ll s[MAXN];void reverse_cantor(ll n, ll k, ll base){    --k;    for (int i = 0, j; i < n; i++)    {        ll t = k / fac[n - i - 1];        for (j = 1; j <= n; j++)        {            if (!vis[j])            {                if (t == 0)                {                    break;                }                --t;            }        }        s[i] = j + base;        vis[j] = true;        k %= fac[n - i - 1];    }}bool check(ll n){    if (n == 0)    {        return false;    }    while (n > 0)    {        if (n % 10 != MAGIC_4 && n % 10 != MAGIC_7)        {            return false;        }        n /= 10;    }    return true;}void init(){    fac[0] = pow[0] = 1;    for (int i = 1; i < MAX_FAC; i++)    {        fac[i] = fac[i - 1] * i;    }    for (int i = 1; i < MAX_POW; i++)    {        pow[i] = pow[i - 1] * 2;    }}int main(){    init();    scanf("%lld%lld", &n, &k);    if (n < MAX_FAC && fac[n] < k)    {        puts("-1");        return 0;    }    int cnt = 1;    while (fac[cnt] < k)    {        cnt++;    }    ll pos = n - cnt, sta = pos + 1;    ll ans = dp(pos);    reverse_cantor(cnt, k, pos);    for (ll i = sta; i <= n; i++)    {        if (check(i) && check(s[i - sta]))        {            ans++;        }    }    printf("%lld\n", ans);    return 0;}
原创粉丝点击