题目链接:https://codeforces.com/problemset/problem/991/E
思路:
多重集合的排列
前置知识:比如说有1,2,3三个数字那么他们构成的排列为全排列:1 2 3,1 3 2,1,2 1 3,2 3 1,3 1 2,3 2 1.
那么就是n!(n=3)321=6种.
那么如果说是有重复的,比如说1 1 2 3,那么就是
也就是4!/(2!*1!*1!*1!)种情况了.
那么这道题里面把每个数字出现的次数存储起来,然后dfs一下.sum为全部数字(包括前导0的情况数目)也就是n!/(num0!num1!…*numn!).sum0为前导0的情况数目也就是(n-1)!/((num0-1)!num1!…*numn!)种情况,sum-sum0就是合法的方案数.
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6+10,mod = 1e9+7;
ll G[N],num[15],a[15];
//预处理阶乘
void init(){
G[0]=1;
for(int i=1;i<1050;i++)
G[i]=(G[i-1]*i);
}
ll ans;
void dfs(int step,int n){
if(step == 10){
ll sum=G[n],sum0=0;
for(int i=0;i<10;i++)
sum/=G[num[i]];
if(num[0]){
sum0=G[n-1]/G[num[0]-1];
for(int i=1;i<10;i++)
sum0/=G[num[i]];
}
ans+=sum-sum0;
return;
}
if(a[step]){
//因为要保证每个数字都要有,所以从1开始枚举
for(int i=1;i<=a[step];i++){
num[step]=i;
dfs(step+1,n+i);
num[step]=0;
}
}
else{
num[step]=0;
dfs(step+1,n);
}
}
int main(){
init();
ll n;
scanf("%lld",&n);
while(n){
a[n%10]++;
n/=10;
}
dfs(0,0);
printf("%lld\n",ans);
}