luoguP1384 幸运数与排列(康托展开)

传送门
我这个人一向不喜欢在洛谷里写题解 但是刚才 80 80 80分看了半个点题解没找到任何有用的信息 发现题解写的都奇奇怪怪的 真的误导我这种不知道错哪里的 所以我决定自己写一个
首先第一眼看到这个题以为是暴力题 ( ( (我看这个题是在一个大一新手同学那里看的 我以为是简单题
结果数据是 1 e 9 1e9 1e9 给我吓一跳
冥思苦想后发现 k k k也是 1 e 9 1e9 1e9的 考虑到 13 ! = 6227020800 13!=6227020800 13!=6227020800 那就意味着当 n n n很大时 最多也只有后面 13 13 13位会参与全排列 前面的根本不会动
所以这个题就很简单了
n > 13 n>13 n>13时,就将后 13 13 13个数进行全排列
对于前面的数 因为数根本没有动 因此幸运数本身的位置也是幸运数 输出幸运数的个数即可
对于后面的数 直接一个一个判断就完事了
n ≤ 13 n\le13 n13时 直接进行全排列 一个一个判断

对于求全排列的方法 这里应用到逆康托展开 这点其他题解里都说的挺明白的 就不细说了
不会康托展开的仔细想想也能想出来 就是个找规律问题

我为什么错呢 因为当 n ≤ 13 n\le13 n13时 我没判断当 4 4 4 7 7 7的位置和 7 7 7 4 4 4的位置
为什么说题解写的不好呢 因为大多数题解在计数 n > 13 n>13 n>13时 前面没动的幸运数应用到了数位 d p dp dp
但其实对于 i i i位数的幸运数只有 2 i 2^i 2i个 一共也只有 1022 1022 1022个幸运数 直接暴力枚举幸运数即可 完全没必要用数位 d p dp dp 只会疑惑看代码的人 不知道为什么题解除了第一个清一色数位 d p dp dp
第一个题解为什么我也觉得不好呢 因为他在查前面没动的幸运数非常麻烦 我真的不知道为什么 那哥们自己都说了幸运数最多也不超过 2 9 2^9 29个 直接查就完事了呗 为啥还要分那么多情况
上代码↓

#include
using namespace std;
typedef long long ll;
ll fac[20];
void set_fac(){
    fac[0]=1LL;
    for(int i=1;i<=13;i++)fac[i]=fac[i-1]*i;
}
int seq[20];
void decontor(int n, int p){
    p-=1;
    vector<int> res,ans;
    for(int i=1;i<=n;i++)res.push_back(i);
    for(int i=n;i;i--){
        int now=p/fac[i-1];p%=fac[i-1];
        ans.push_back(res[now]);
        res.erase(res.begin()+now);
    }
    for(int i=1;i<=n;i++)seq[i]=ans[i-1];
}
int lnum[5000],pow2[20],num=2;
void set_lnum(){
    pow2[0]=1;
    for(int i=1;i<=9;i++)pow2[i]=pow2[i-1]<<1;
    pow2[0]=0;lnum[1]=4,lnum[2]=7;
    for(int i=1;i<=8;i++){
        int fst=num-pow2[i]+1,lst=num;
        for(int j=fst;j<=lst;j++)
            lnum[++num]=lnum[j]*10+4,lnum[++num]=lnum[j]*10+7;
    }
    lnum[num+1]=int(1e9)+1;
}
bool check(int x){
    while(x){
        if(x%10!=4&&x%10!=7)return false;
        x/=10;
    }
    return true;
}
int n,k,ans;
int main(){
    cin>>n>>k;
    set_lnum();set_fac();
    if(n<13&&k>fac[n]){puts("-1");return 0;}
    decontor(min(n,13),k);
    if(n<=13){
        if(seq[4]==4)ans++;
        if(seq[7]==7)ans++;
        if(seq[4]==7)ans++;
        if(seq[7]==4)ans++;
    }
    else{
        for(int i=1;lnum[i]<=n-13;i++)ans++;
        for(int i=n-12;i<=n;i++){
            if(check(i)&&check(seq[i-n+13]+n-13))ans++;
        }
    }
    cout<<ans<<endl;
}

你可能感兴趣的:(数论,动态规划,算法,c++)