51nod 1635 第K个幸运排列

51nod 1635 第K个幸运排列_第1张图片

题意:求1-N的全排列中的第K种排列,其中有多少幸运数字处在幸运位置上。幸运数字以及位置的定义为只包含4和7

思路:我们知道13!大于1e9,根据全排列的顺序显然最多只有13个位置在发生重排,那么我们可以根据输入的K将N个位置分成两个部分:前半部分为固定不动的部分(即1,2,3,4,……),后半部分为实际发生了重排的部分,我们对这两个部分分别计算满足要求的个数。

   我们首先找到第一个数X,它的阶乘大于K,这个数就是发生重排的数的个数,通过这个数我们将N个位置分为左右两部分。前半部分即从1到N-X的数列,问题就变成了1到N-X有多少个数只包含4和7,这里可以数位dp或者直接搞,直接搞的话首先算出N-X有多少位(假设有Len位),对于1位数到Len-1位数,随意放4或7即可,也就是2的1次方加到2的Len-1次方,对于恰好Len位的数,我们从高位开始拆解,如果当前位大于7,那么这一位4和7都能放,后面随便放;如果当前位等于7,那么这一位4一定能放,7能不能放继续看下一位数;如果当前位是5和6,那么这一位放4,后面随便放;如果当前位为4,接着看后面;当前位小于4那么这一位没法放数了,结束计数。

   后半部分因为数的个数非常少,我们将K进行逆康托展开,还原出后半部分原来的排列,对位置和数都check一下是否只包含4和7,统计一下满足的个数。

   最后两部分计数加起来就是答案了。K大于N!的输出-1。

代码:

#include
using namespace std;
typedef long long LL;
LL fac[20],p[40];
int a[20];
LL solve(LL x) {
    if (x==0) return 0;
    int pos=0;
    while (x) a[++pos]=x%10,x/=10;
    LL res=0;
    for (int i=pos;i>=1;i--) {
        if (a[i]>7) {
            res+=2*p[i-1];
            break;
        }else if (a[i]==7) {
            res+=p[i-1];
            if (i==1) res++;
        }else if (a[i]>4) {
            res+=p[i-1];
            break;
        }else if (a[i]==4) {
            if (i==1) res++;
        }else if (a[i]<4) {
            break;
        }
    }
    for (int i=1;i<=pos-1;i++) res+=p[i];
    return res;
}
bool vis[20];
int s[20];
void reverse_cantor(int n,LL k,int base) {
    int i,j,t;
    --k;
    for (i=0;i0) {
        if (n%10!=4 && n%10!=7) return false;
        n/=10;
    }
    return true;
}
int main() {
    fac[0]=1;p[0]=1;
    for (int i=1;i<=13;i++) {
        fac[i]=fac[i-1]*i;
    }
    for (int i=1;i<=32;i++) p[i]=p[i-1]*2;
    LL n,k;
    scanf("%lld%lld",&n,&k);
    if (n<13 && fac[n]=10) tmp/=10,len++;
    LL ans=solve(pos);
    reverse_cantor(n-pos,k,pos);
    for (int i=start;i<=n;i++) {
        if (check(i) && check(s[i-start])) ans++;
    }
    printf("%lld\n",ans);
    return 0;
}


你可能感兴趣的:(51nod)