牛客练习赛13 D幸运数字Ⅳ . 康托逆展开
题意:中文题面。。
题解:13的阶乘大于1e9,也就是说如果n大于13,那么只有后半部分大于13 的会进行重排,所以可以把一个数列分为两段,第一段是1到n-12,第二段是n-12到n。我们需要先dfs出幸运数字的所有情况,sort一下,枚举过去如果n-12大于幸运数字则ans++(因为前一段未发生变化,所以在1到n-12里面的幸运数字的下标和其本身是相同的),接下来就只要要求后半段的序列的第k个序列这就用到了康托逆展开(以前学长教过,没想到居然真用上了。。但是敲不仔细,WA了一片)。康托展开是求一个序列他是第几个全排列,逆展开就是求第几个去全排列是谁。具体的去看百度百科吧。。
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <map> #include <queue> #include <vector> #include <cstring> #include <iomanip> #include <set> #define se second #define fi first #define ll long long #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define Pii pair<int,int> #define ull unsigned long long #define pb push_back #define fio ios::sync_with_stdio(false);cin.tie(0) const double Pi=3.14159265; const double e=2.71828182; const int N=1e5+5; const ull base=163; using namespace std; int str[N]; ll a[N]; int tot=0; map<ll,int>mp; map<ll,int>Bo; ll c[1500]; void dfs(ll x){ if(x>=1000000000)return ; if(x!=0){ mp[x]=1; c[tot++]=x; } dfs(x*10+4); dfs(x*10+7); } int main(){ fio; int n,k; cin>>n>>k; ll sum=1; dfs(0); sort(c,c+tot); for(ll i=1;i<=n;i++){ sum*=i; a[i]=sum; if(sum>=1000000000)break; } if(sum<k)return cout<<-1<<endl,0; k--; int cnt=(n>13?n-12:1); ll m=k; ll mid=k; int ans=0; for(int i=0;i<tot;i++){ if(c[i]>=cnt)break; else{ ans++; } } int num=1; for(ll i=(n>13?13:n)-1;i>=1;i--){ m=mid/a[i]; mid=mid%a[i]; int tot=0; for(int j=(n>13?cnt:1);j<=n;j++){ if(!Bo[j])tot++; if(tot==m+1){ Bo[j]=1,str[num++]=j; break; } } } for(int i=(n>13?cnt:1);i<=n;i++) if(!Bo[i]){ str[num]=i; break; } for(int i=1;i<=num;i++){ if(mp[i+cnt-1]&&mp[str[i]]){ ans++; } } cout<<ans<<endl; return 0; }