牛客练习赛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;
}

 

转载于:https://www.cnblogs.com/Mrleon/p/8594626.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值