Codeforces-121C(逆康托展开)

 

题目大意:

给你两个数n,k求n的全排列的第k小,有多少满足如下条件的数:

首先定义一个幸运数字:只由4和7构成

对于排列p[i]满足i和p[i]都是幸运数字

思路:

对于n,k<=1e9

一眼逆康托展开

什么?你不知到康托展开?

点这里点这里点这里

由于阶乘的增长是非常快的

13的阶乘就大于1e9了

所以说:

对于一个n的权排列 1  2 3 4 ...... n

我们最多动他的后13位就可以得到第k小的排列

我们称之为动n的后x位可以得到第k小的排列(如果这里都取13的话,有的序列是n<13的,会越界)

然后我们对[1,n-x]中的数字统计答案的时候可以数位dp,可以dfs

然后对后x位统计答案,就是裸的逆康托展开了

Code:

ll n, k, x, fac[20], ans;
std::vector<ll> v;
ll suf[20], cnt;
void dfs(ll num, ll top)
{
    // cout<<top<<endl;
    if(num > top) return ;
    if(num <= top && num != 0)ans++;
    dfs(num * 10 + 4, top);
    dfs(num * 10 + 7, top);
}
int ok(ll x)
{
    int flag = 1;
    while(x)
    {
        int yy = x % 10;
        // cout<<yy<<"@"<<endl;
        if(yy != 4 && yy != 7) flag = 0;
        x /= 10;
    }
    return flag;
}
void re_count()
{
    sort(v.begin(), v.end());
    for(int i = x ; i >= 1 ; i--)
    {
        ll pos = k / fac[i - 1];
        k = k % fac[i - 1];
        suf[++cnt] = v[pos];
        v.erase(v.begin() + pos);
    }

}
int main()
{
    fac[0] = 1;
    rep(i, 1, 16) fac[i] = i * fac[i - 1];
    n = read(), k = read();
    k--;
    for(int i = 1 ; i <= 16; i++)
    {
        if(fac[i] >k)
        {
            x = i;
            break;
        }
    }
    //  cout<<x<<"#"<<endl;
    for(int i = n; i >= n - x + 1; i--)  v.push_back(i);
    if(n - x < 0)
    {
        cout << -1;
        return 0;
    }
    dfs(0, n - x); //搜出n-x的幸运数
    re_count();
    for(int  i = n - x + 1; i <= n; i++) if(ok(i) && ok(suf[i - (n - x)])) ans++;
    out(ans);
    return 0;
}
View Code

 

posted @ 2021-04-01 14:07  UpMing  阅读(91)  评论(0)    收藏  举报