洛谷P1384 幸运数与排列

观察到
对于过大的n,至多只有最后13位会变
对于会变得位置求Cantor展开,然后直接计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<cstdio>
#include<cstdlib>
#define LL long long
using namespace std;
const int N=15;
int n,m,w[N],used[N],ans=0,tot;
LL fact[N]={1};
int get(int k)
{
for(int i=1,num=0;i<=tot;i++)
{
if (!used[i]) num++;
if (num==k) return i;
}
}
int work(int x)
{
if (!x) return 0;
for(;x;x/=10)
if (x%10!=7&&x%10!=4) return 0;
return 1;
}
void check()
{
LL num=1;
for(int i=1;i<=n;i++)
{
num*=i;
if (num>=m) return;
}
puts("-1"),exit(0);
}
int main()
{
scanf("%d%d",&n,&m);
check();
for(int i=1;i<N;i++) fact[i]=fact[i-1]*i;
for(int i=0;i<N;i++)
if (fact[i]>=m) {tot=i;break;}
m--;
for(int i=tot;i>0;i--)
{
w[tot-i+1]=get(m/fact[i-1]+1)+n-tot;
used[w[tot-i+1]-n+tot]=1,m%=fact[i-1];
}
for(int len=1;len<N;len++)
for(int i=0;i<(1<<len);i++)
{
LL num=0;
for(int j=1;j<=len;j++)
num=num*10+((i>>(j-1))&1?7:4);
if (num>n) continue;
ans+=work(num+tot<=n?num:w[num-n+tot]);
}
printf("%d",ans);
return 0;
}