牛客练习赛13 幸运数字Ⅳ 康拓展开

链接:https://www.nowcoder.com/acm/contest/70/D
来源:牛客网

定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
现在想知道在1…n的第k小的排列(permutation,https://en.wikipedia.org/wiki/Permutation)中,有多少个幸运数字所在的位置的序号也是幸运数字。
输入描述:

第一行两个整数n,k。
1 <= n,k <= 1000,000,000

输出描述:

一个数字表示答案。
如果n没有k个排列,输出-1。

示例1
输入

7 4

输出

1

说明

1 2 3 4 6 7 5

示例2
输入

4 7

输出

1

说明

2 1 3 4

解题思路 k<1e9 13! > 1e9
所以枚举全排列的时候,只需要枚举后最后的13个数字即可。
一个个暴力的去枚举复杂度是13!
利用逆康拓展开就能很轻松的求得 第k大的全排列。
然后暴力判断即可 , 判断完最后13个数后。
再数一下前面顺序没有动的序列中有多少个幸运数即可
复杂度O(n)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
using namespace std;
const int MAX=1e5+10;
long long num[MAX];
int tot;
void dfs(long long now,long long ceng){
    if(ceng>10) return ;
    num[tot++]=now;
    dfs(now*10+4,ceng+1);
    dfs(now*10+7,ceng+1);
}
bool check(long long n,long long k){
    long long ans=1;
    for(long long  i=1;i<=n;i++){
        ans*=i;
        if(ans>=k) return 1;
    }
    return 0;
}
bool judge(long long s){
    while(s){
        if(s%10!=4 && s%10!=7) return 0;
        s/=10;
    }
    return 1;
}
long long fac[15];
long long ans[15];
//index表示给定的第几个数,结果序列保存在ans数组
void cantor_reverse(int index, int n, long long *ans){
    index--;
    bool vis[15];//标记
    memset(vis, false, sizeof(vis));
    for (int i = 0; i < n; i++){
        int tmp = index /fac[n - i - 1];
        for (int j = 0; j <= tmp; j++)
            if (vis[j])
                tmp++;
        ans[i] = tmp + 1;
        vis[tmp] = true;
        index %= fac[n - i - 1];
    }
}
int main(){
    int n,k;
    cin>>n>>k;
    if(!check(n,k)){
        puts("-1");
        return 0;
    }
    dfs(4,0);
    dfs(7,0);
    fac[0]=fac[1]=1;
    for(long long i=1;i<=13;i++){
        fac[i]=fac[i-1]*i;
    }
    int nums=0;
    int top=min(n,13);
    cantor_reverse(k,top,ans);
    for(int i=0;i<top;i++){
        //cout<<ans[i]+n-top<<" ";
        if(judge(n-top+i+1) && judge(ans[i]+n-top)){
            nums++;
        }
    }
    for(int i=0;i<tot;i++){
        if(num[i]<=n-top){
            nums++;
        }
    }
    cout<<nums<<endl;
}

猜你喜欢

转载自blog.csdn.net/lifelikes/article/details/79617193