[HAOI2010]计数

数位DP逐位确定的思想

因为我们要求小于当前数的个数,位数不即它的,可以认为有前导零

所以可以枚举每一位(给定数的位数),再枚举当前位填什么,

当填的数小于给定数的这一位时,后面可以直接用组合数,因为填什么,怎么填都符合要求(小于给定数),就是剩下的数集中还没有被填的数都要做组合数(这个求法很妙???)

当填的数等于给定数的这一位时,此时值为一,不能计算,要推下去(就是下面再小于等于都是基于这一位等于来的)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<queue>
 7 #include<set>
 8 #include<map>
 9 #include<vector>
10 #include<stack>
11 using namespace std;
12 typedef long long ll;
13 const int maxn=57;
14 ll ans,n;
15 ll a[maxn],cnt[maxn],C[maxn][maxn];
16 char s[maxn];
17 ll get(int n){
18   ll ret=1;
19   for(int i=0;i<=9;i++){
20     if(cnt[i]){
21       ret*=C[n][cnt[i]];
22       n-=cnt[i];
23     }
24   }
25   return ret;
26 }
27 int main(){
28   scanf("%s",s+1);n=strlen(s+1);
29   for(int i=1;i<=n;i++){
30     a[i]=s[i]-'0';cnt[a[i]]++;
31   }
32   C[0][0]=1;
33   for(int i=0;i<=51;i++){
34     for(int j=0;j<=51;j++){
35       C[i+1][j]+=C[i][j];C[i+1][j+1]+=C[i][j];
36     }
37   }
38   for(int i=1;i<=n;i++){
39     for(int j=0;j<a[i];j++){
40       if(cnt[j]){
41         cnt[j]--;ans+=get(n-i);cnt[j]++;
42       }
43     }
44     cnt[a[i]]--;
45   }
46   cout<<ans<<endl;
47 }

 

posted @ 2018-11-08 09:39  lcan  阅读(132)  评论(0编辑  收藏  举报