1. 程式人生 > >牛客練習賽13 幸運數字Ⅳ 康拓展開

牛客練習賽13 幸運數字Ⅳ 康拓展開

定義一個數字為幸運數字當且僅當它的所有數位都是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; }