bzoj 4667 小y的密码

排列/组合计数.

  • 限制是否满足只与 $0\sim 9$ 这些数字各自出现了多少次有关.所以可以 $dfs$ 大力枚举这些数字各自的出现次数.
  • 若限制满足,再计算用这些数字能组合出多少 $\leq n$ 的数.
  • 若这些数个数不足 $n$ 的位数,那么就是带重复元素的排列数.注意减掉有前导 $0$ 的情况.
  • 若个数达到了 $n$ 的位数,就枚举从哪一位开始可以不用考虑限制(就相当于数位 $dp$ 里面那个 $limit$ ).
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
int n,k,limit;
int t,pw[11];
int a[11],tot[11],fig[11],m=0;
int fac[11];
ll ans=0;
bool check()
{
if(!m || !a[m])
return false;
int mid=a[(m+1)>>1];
ll sum=0;
for(int i=1;i<=m && sum<=limit;++i)
{
ll tmp=1;
for(int j=1;j<=k;++j)
tmp*=a[i]-mid;
sum+=tmp;
}
return sum<=limit;
}
int calc(int x)
{
int res=fac[x];
for(int i=0;i<=9;++i)
res/=fac[tot[i]];
return res;
}
void solve()
{
memset(tot,0,sizeof tot);
for(int i=1;i<=m;++i)
++tot[a[i]];
if(m<t)
{
ans+=calc(m);
if(tot[0])
{
--tot[0];
ans-=calc(m-1);
}
}
else
{
bool flag=true;
int tmp=t;
for(int i=t;i>=1;--i)
{
for(int j=(i==t);j<fig[i];++j)
if(tot[j])
{
--tot[j];
--tmp;
ans+=calc(tmp);
++tot[j];
++tmp;
}
if(!tot[fig[i]])
{
flag=false;
break;
}
--tot[fig[i]];
--tmp;
}
ans+=(int)flag;
}
}
void dfs(int x)
{
if(check())
solve();
if(m==t)
return;
for(int i=x;i<=9;++i)
{
++m;
a[m]=i;
dfs(i);
--m;
}
}
int main()
{
fac[0]=1;
for(int i=1;i<=9;++i)
fac[i]=fac[i-1]*i;
n=read(),k=read(),limit=read();
while(n)
fig[++t]=n%10,n/=10;
dfs(0);
cout<<ans<<endl;
return 0;
}