题目链接:
题目大意:
问字典序第k大的1~n的排列中的只由4和7组成的数为位置上具有相同性质的数的个数。
题目分析:
- 首先因为 k≤109 ,所以需要变化的位最多只有13位,因为13位就可以导致字典序推进13!
- 所以除了末尾的13个数位置有变化,其他的数的位置都没有变化,所以我们直接枚举符合条件的数的个数即可,因为他们的数值和下标相同,然后对于最后的13位,我们可以利用组合数学快速求出第k个排列的末尾13位,然后直接枚举这13位,然后判断合法不合法即可。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
int ans,n,k,m,x;
LL fac[15];
int num[15];
bool mark[15];
void dfs ( int i = 0 , LL num = 0 )
{
if ( num && num <= m ) ans++;
if ( i == 9 )
return;
dfs ( i+1 , num*10+4 );
dfs ( i+1 , num*10+7 );
}
void make ( int i = 1 )
{
if ( i > x ) return;
int j = 1,t;
while ( mark[j] ) j++;
while ( k >= fac[x-i] )
{
j++;
while ( mark[j] ) j++;
k -= fac[x-i];
}
while ( mark[j] ) j++;
num[i] = j+m;
mark[j] = 1;
make( i+1 );
}
int main ( )
{
fac[0] = 1LL;
for ( LL i = 1 ; i < 15 ; i++ )
fac[i] = fac[i-1]*i;
while ( ~scanf ( "%d%d" , &n , &k ) )
{
ans = 0;
memset ( mark , 0 , sizeof ( mark ) );
if ( n < 15 && fac[n] < k )
{
puts ( "-1" );
continue;
}
for ( int i = 0 ; i < 15 ; i++ )
if ( fac[i] >= k )
{
x = i;
break;
}
m = n-x;
dfs ( );
k--;
make( );
for ( int i = m+1 ; i <= n ; i++ )
{
bool flag = true;
int temp = i;
while ( temp )
{
if (temp%10 != 4 && temp%10 != 7 ) flag = false;
temp /= 10;
}
temp = num[i-m];
while ( temp )
{
if ( temp%10 != 4 && temp%10 != 7 ) flag = false;
temp /= 10;
}
if ( flag ) ans++;
}
printf ( "%d\n" , ans );
}
}