思路:对于比当前数字长度小的数字其实可以看成前导0,例如1230和123,其实可以看做1230,0123.这样的话题目就简化成了长度为n的数字的全排列中有多少个小于当前数。首先先介绍可重集全排列怎么求,先看一下我的理解,字有点丑只能忍忍了。。。
那么关键我们怎么求可重集全排列中小于当前数的个数呢?这里其实也用的了数位DP的思想,设当前数为X,我们可以枚举X的每一位,假设当前为i,那么如果当前位是【0,i-1】的话是不是后面无论怎样的排列都会比X要小,所以总的思路就是遍历X的每一位,再遍历小于当前i的所有数(也就是【0,i】)对他们进行可重集全排列计算,答案累加就行,不过就是要注意一下每个数的个数一些细节就行,于是就可以上代码了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50;
ll c[maxn][maxn],cnt[maxn],ans;
char s[maxn];
int main()
{
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;++i) cnt[s[i]-'0']++;
c[0][0]=c[1][0]=c[1][1]=1;
for(int i=2;i<maxn;++i)
{
c[i][0]=1;
for(int j=1;j<maxn;++j)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
for(int i=1;i<=len;++i)
{
for(int j=0;j<s[i]-'0';++j)
{
if(cnt[j]>=1)
{
ll res=1;
int sum=len-i;
cnt[j]--;//要注意的就是这个地方,我们要先把这个数拿出来才能计算
for(int k=0;k<10;++k) res*=c[sum][sum-cnt[k]],sum-=cnt[k];
cnt[j]++;//最后加回去变回原样
ans+=res;
}
}
cnt[s[i]-'0']--;
}
printf("%lld\n",ans);
}