zoukankan      html  css  js  c++  java
  • P1384 幸运数与排列

    传送门

    发现 $k<=10^9<13!$ 所以只有最后几位会变

    前面一大段都是固定的

    考虑求前面一大段的贡献

    $n$最大就 9 位,直接数位DP就好了(爆搜也是可以的)

    考虑后面几位的贡献

    用逆康托展开求出每一位的值然后暴力判断就好了

    代码有一些细节:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=233;
    int n,m,ans,a[N],f[N];
    ll fac[N];
    int dfs(int pos,bool lim,bool z)//lim表示前几位是否在上限,z前几位表示是否是前导零
    {
        if(pos==-1) return 1;
        if(!lim&&!z&&f[pos]!=-1) return f[pos];
        int res=0,mx=lim ? a[pos] : 9;
        for(int i=0;i<=mx;i++)
        {
            if(i==4||i==7||(z&&i==0))
                res+=dfs(pos-1,lim&(i==mx),z&&i==0);
        }
        if(!lim&&!z) f[pos]=res;
        return res;
    }
    int solve1(int x)//求前面一段的贡献
    {
        int tot=0;
        while(x) a[tot++]=x%10,x/=10;
        return dfs(tot-1,1,1);
    }
    inline bool pd(int x)//判断是否为幸运数
    {
        while(x)
        {
            if(x%10!=4&&x%10!=7) return 0;
            x/=10;
        }
        return 1;
    }
    int solve2(int x,int rnk)//求后面一段贡献
    {
        vector <int> v,b;
        for(int i=n-x+1;i<=n;i++) v.push_back(i);
        for(int i=x;i>=1;i--)//逆康托展开
        {
            int t=rnk/fac[i-1]; rnk%=fac[i-1];
            sort(v.begin(),v.end());
            b.push_back(v[t]); v.erase(v.begin()+t);
        }
        int p=n-x+1,res=0,len=b.size();
        for(int i=0;i<len;i++,p++)
            if(pd(p)&&pd(b[i])) res++;//暴力判断
        return res;
    }
    int main()
    {
        n=read(),m=read();
        fac[0]=1; for(int i=1;i<=15;i++) fac[i]=fac[i-1]*i;
        if(n<=12&&fac[n]<1ll*m) { printf("-1"); return 0; }//注意特判无解
        memset(f,-1,sizeof(f));
        int tot=0; while(m>fac[tot]) tot++;
        printf("%d",solve1(n-tot)-1/*-1是因为数位dp时全取0也会算到贡献*/+solve2(tot,m-1));
        //注意m-1,因为康托展开求的是比一个排列小的排列数,即它的排名为比它小的排列数+1
        return 0;
    }
  • 相关阅读:
    java实现快速排序
    java实现简单回文算法
    HTML条件注释
    LeetCode 将有序数组转换为二叉搜索树
    LeetCode 有效的数独
    Leetcode 买卖股票的最佳时机 II
    模拟登陆163邮箱
    oracle 视图的相关操作
    oracle 约束类别
    软件测试用例知识点梳理
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10342881.html
Copyright © 2011-2022 走看看