BZOJ 4667 小y的密码

来源:互联网 发布:自动识别字体软件 编辑:程序博客网 时间:2024/06/04 18:51

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 longint 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;}


原创粉丝点击