Description
废话不多说,反正小y高考屠场啦!!
然而我们知道,高考报志愿系统是需要密码的,而小y根本没有在意自己的密码,为了挑战人生,他决定自己把密码找出来。
不知道为什么,小y认为自己的密码是一个大小不超过n的正整数,且满足一个奇怪的性质。
设这个正整数各个数位上的数字按数值大小从小到大依次为a_1,a_2,…,a_m,那么就有

其中k和Limi是给定的常数。现在给定n、k、Limi,求有多少种可能的密码。
Input
一行三个非负整数n、k、Limi,用一个空格隔开
n<=10^9,k<=10, Limi<=2*10^9
Output
一行一个整数表示符合条件的不同密码数量。
Sample Input
100000 0 5
Sample Output
99999
HINT
Source
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
数位DP+dfs+思路~
注意到答案只与0~9每个数出现的次数有关,所以我们dfs出所有满足条件的数的组合,再利用数位DP和组合数计数来计算答案即可。
数位DP实在是太太太太太神奇辣!最好看一看代码!
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
int n,k,lim,a[12],c[12],tot,ans,inc[12],num[12];
bool che()
{
if(!tot || !c[tot]) return 0;
ll now=0,kkz=c[(tot+1)/2],tmp;
for(int i=1;i<=tot;i++)
{
tmp=1;
for(int j=1;j<=k;j++) tmp*=(ll)c[i]-kkz;
now+=tmp;
}
return now<=lim;
}
int calcas(int u)
{
int now=inc[u];
for(int i=0;i<=9;i++) now/=inc[num[i]];
return now;
}
void cal()
{
memset(num,0,sizeof(num));
for(int i=1;i<=tot;i++) num[c[i]]++;
if(tot<a[0])
{
ans+=calcas(tot);
if(num[0])
{
num[0]--;ans-=calcas(tot-1);
}
return;
}
int flag=1,tmp=tot;
for(int i=tot;i;i--)
{
for(int j=(i==tot);j<a[i];j++)
if(num[j])
{
num[j]--;tmp--;
ans+=calcas(tmp);
num[j]++;tmp++;
}
if(!num[a[i]])
{
flag=0;break;
}
num[a[i]]--;tmp--;
}
ans+=flag;
}
void dfs(int u)
{
if(che()) cal();
if(tot==a[0]) return;
tot++;
for(int i=u;i<=9;i++) c[tot]=i,dfs(i);
tot--;
}
int main()
{
scanf("%d%d%d",&n,&k,&lim);
while(n) a[++a[0]]=n%10,n/=10;inc[0]=1;
for(int i=1;i<=a[0];i++) inc[i]=inc[i-1]*i;
dfs(0);
printf("%d\n",ans);
return 0;
}