链接:
https://www.nowcoder.com/acm/contest/70/D
来源:牛客网
来源:牛客网
题目描述
定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
现在想知道在1...n的第k小的排列(permutation,https://en.wikipedia.org/wiki/Permutation)中,有多少个幸运数字所在的位置的序号也是幸运数字。
比如说,47、744、4都是幸运数字而5、17、467都不是。
现在想知道在1...n的第k小的排列(permutation,https://en.wikipedia.org/wiki/Permutation)中,有多少个幸运数字所在的位置的序号也是幸运数字。
输入描述:
第一行两个整数n,k。 1 <= n,k <= 1000,000,000
输出描述:
一个数字表示答案。 如果n没有k个排列,输出-1。
题解:
逆康托展开,求出第k个排列,因为n很大,但是k即使最大最多
影响到排列的最后13位。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll fac[14],ans,a[14],n;
bool vis[14];
ll min1(ll a,ll b)
{
if(a<b)return a;
return b;
}
void dfs(ll t,ll r)//dfs求解每一个小于r的幸运数字
{
if(t>r)return;
if(t)ans++;
dfs(10*t+4,r);
dfs(10*t+7,r);
}
void kang(ll m,ll k)//求最后m位的康托展开
{
k--;int i,j;
memset(vis,0,sizeof(vis));
for(i=1;i<=m;i++)
{
ll t=k/fac[m-i];
for(j=1;j<=m;j++)
{
if(!vis[j])
{
if(!t)break;
t--;
}
}
a[i]=n-m+j;
vis[j]=1;
k=k%fac[m-i];
}
}
bool check(ll r)
{
while(r)
{
if(!(r%10==4||r%10==7))
return 0;
r=r/10;
}
return 1;
}
int main()
{
fac[0]=1;
for(ll i=1;i<=13;i++)fac[i]=fac[i-1]*i;
ll k;scanf("%lld%lld",&n,&k);
if(n<=13&&fac[n]<k)printf("-1\n");
else
{
ll m=min1(n,13);
ans=0;
if(m<n)dfs(0,n-m);
kang(m,k);
for(ll i=1;i<=m;i++)
if(check(n-m+i)&&check(a[i]))
ans++;
printf("%lld\n",ans);
}
return 0;
}