E. Bus Number[暴力+组合数学]

E. Bus Number

知识点:LL可以存到20!,n个箱子,m个球,不允许空箱的模型

题意:给定一个数n(n->1e18),问其中每种数字出现至少一次,且不超过总次数,且没有前导零有多少种组合情况

思路:暴力枚举每个数字出现的次数,接下来就是n个箱子,m个球,不允许有空箱子的模型.想题的时候想到这个模型,却不知道怎么做,基础的组合数学... 复杂度cnt[1]*cnt[2]*---- cnt[9]*10

#include<bits/stdc++.h>
#define PI acos(-1.0)
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int N=4e5+5;
const int MOD=1e9+7;
ll a[N],sum[N];
ll cnt[50];
ll pro[50];

ll c(int n,int m){//计算组合数
    return pro[n]/pro[m]/pro[n-m];
}

ll cul(){
    ll n=0;
    ll ans=1;
    for(int i=0;i<=9;i++)   n+=a[i];
    for(int i=0;i<=9;i++){//上述放球模型
        ans*=c(n,a[i]);
        n-=a[i];
    }
    return ans;
}

inline ll dfs(int num){
    if(num==10){
        ll res=cul();
//        cout <<res << endl;
        if(a[0]>0){// 对于存在0的情况.把a[0]情况下的res 减去 第一位是0的情况
            a[0]--;
            res-=cul();
            a[0]++;
        }
        return res;
    }

    if(cnt[num]==0){
        a[num]=0;
        return dfs(num+1);
    }

    ll ans=0;
    for(int i=1;i<=cnt[num];i++){
        a[num]=i;
        ans+=dfs(num+1);
    }
    return ans;
}

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    pro[0]=pro[1]=1;
    for(int i=2;i<=20;i++)  pro[i]=i*pro[i-1];
    ll n;
    cin>>n;
    while(n){
        cnt[n%10]++;
        n/=10;
    }
    cout << dfs(0)<<"\n";

    return 0;
}



猜你喜欢

转载自blog.csdn.net/haipai1998/article/details/80792168