[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 }