链接: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;
}