【洛谷】P2518 [HAOI2010]计数(可重集全排列+简单计数)

在这里插入图片描述
思路:对于比当前数字长度小的数字其实可以看成前导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);
 } 
发布了70 篇原创文章 · 获赞 0 · 访问量 2444

猜你喜欢

转载自blog.csdn.net/qq_42479630/article/details/104149480