隔板法

例一:

例二:

E. 分装组合


Description

老师交给小H 一个任务:有 n 个相同的雷神珠和 m 个不同的宝箱,一共有几种装箱方法(每个盒子至少一个),由于数据很大,现在对于给定的 n 和 m,请输出 mod 100003 结果

Input

只有一行,两个非负整数n,m1n,m1100

Output

1 个非负整数

Samples

Input Copy
5 3
Output
6

这个题就是每个箱子至少放一个球,然后你可以转化为每一个箱子先放一个球,然后就转化为有n-m个球然后,放在m个箱子

中(允许箱子为空的),和上面是一样的

例三:

小王在考试中遇到一道难题:方程a1+a2++an=m的非负整数解有几个,请你帮他算一下(这也可以算作他作弊吧)。

Input

一行,两个以空格隔开的数n,m,表示方程a1+a2++an=m

Output

一行,一个数,表示非负整数解的个数。

Samples

Input Copy
3 4
Output
15

Hint

样例说明:

0,0,4   0,1,3  0,2,2   0,3,1 0,4,0 

1,0,3   1,1,2  1,2,1   1,3,0 

2,0,2    2,1,1  2,2,0 

3,0,1    3,1,0 

4,0,0 

(total=5+4+3+2+1=15)

数据范围 

  • 对于50 %的数据,0n,m100≤n,m≤10,结果<200
  • 对于100 %的数据,0n,m<327670≤n,m<32767, 结果<32767

这个题就是可以看成有m个球放在n个盒子中

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+100; 
const int mod=1000000007;
ll f[maxn];
int n,m;
void inint(){
    f[0]=1;
    f[1]=1;
    for(int i=2;i<=1000000;i++){
        f[i]=(f[i-1]*i)%mod;
    }
}
ll qpow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1){
            ans=(ans*a)%mod;
        }
        a=(a*a)%mod;
        b/=2;
    }
    return ans%mod;
}
ll C(int n,int m){
    if(m==0) return 1;
    return (f[n]*qpow(f[n-m]*f[m]%mod,mod-2)%mod)%mod;
}
int main(){
    inint();
    cin>>n>>m;
    cout<<C(m+n-1,n-1)<<endl;
}

例四:

链接:https://ac.nowcoder.com/acm/contest/1085/J
来源:牛客网

小sun最近对计数问题来了兴趣,现在他有一个问题想问问你:

 

有一个含有n个数字的序列,每个数的大小是不超过1000的正整数,同时这个序列是个单调不增序列。但是很不幸的是,序列在保存过程中有些数字丢失了,请你根据上述条件,计算出有多少种不同的序列满足上述条件,答案对1000000007取模。(具体可以看样例)

输入描述:

第一行包含一个整数n,表示这个序列的长度。
第二行为n个整数aia_iai,用空格隔开,如果数字是0,代表这个数字丢失了,其他的数字都在1~1000之间

输出描述:

输出一行,表示答案。
示例1

输入

复制
3
9 0 8

输出

复制
2
示例2

输入

复制
2
5 4

输出

复制
1
示例3

输入

复制
3
0 0 0

输出

复制
167167000

备注:

1n1e6
0≤ai≤10000

 隔板法:

隔板法就是在n个元素间插入(b-1)个板,即把n个元素分成b组的方法。

比如将20个大小形状完全相同的小球放入3个不同的盒子,允许有盒子为空,但球必须放完,有多少种不同的方法?

解析:将20个小球分成三组需要两块隔板,因为允许有盒子为空,不符合隔板法的原理,那就人为的再加上3个小球,保证每个盒子都至少分到一个小球,那就符合隔板法的要求了(分完后,再在每组中各去掉一个小球,即满足了题设的要求)。然后就变成待分小球总数为23个,球中间有22个空档,需要在这22个空档里加入2个隔板来分隔为3份,共有C_{22}^{2} = 231种不同的方法。

回到本题,对于栗子9 0 0 7,我们可以使用的数字是9 - 7 + 1 = 3个(7,8,9),我们把可使用的数字当成盒子,把丢失的数字当成小球,是不是和上面的栗子一样了,那么答案也就是C_{n+m-1}^{m-1}(n表示丢失数字的个数,m表示可使用的数字)。想想为什么不把可使用的数字当成球,丢失的数字当成盒子呢?其实是因为可以出现重复的数字,举个栗子9,0,0,8,答案为3,可使用的数字为2,丢失的数字也是2,每种情况如下(下面的小球表示零,也就是丢失的数字):

  • 9,9,9,8(将两个小球放9号盒子里)
  • 9,9,8,8(将两个小球分别放到8号和9号盒子里)
  • 9,8,8,8(将两个小球放8号盒子里)
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+100; 
const int mod=1000000007;
ll f[maxn];
int a[maxn];
int n;
void inint(){
    f[0]=1;
    f[1]=1;
    for(int i=2;i<=1000000;i++){
        f[i]=(f[i-1]*i)%mod;
    }
}
ll qpow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1){
            ans=(ans*a)%mod;
        }
        a=(a*a)%mod;
        b/=2;
    }
    return ans%mod;
}
ll C(int n,int m){
    if(m==0) return 1;
    return (f[n]*qpow(f[n-m]*f[m]%mod,mod-2)%mod)%mod;
}
int main(){
    inint();
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int cnt=0,id=0;
    ll ans=1;
    a[0]=1000;
    for(int i=1;i<=n;i++){
        if(a[i]==0){
            cnt++;
        }
        else{
            int n=cnt;
            int m=a[id]-a[i]+1;
            id=i;
            cnt=0;
            ans=ans*(C(n+m-1,m-1)%mod)%mod;
            //cout<<n<<" "<<m<<" "<<ans<<endl;
        }
    }
    if(cnt){
        int n=cnt,m=a[id];
        ans=ans*C(n+m-1,m-1)%mod;
    }
    cout<<ans%mod<<endl;
} 

例题:

视频讲解

传送门

给你一个二维整数数组 queries ,其中 queries[i] = [ni, ki] 。第 i 个查询 queries[i] 要求构造长度为 ni 、每个元素都是正整数的数组,且满足所有元素的乘积为 ki ,请你找出有多少种可行的方案。由于答案可能会很大,方案数需要对 109 + 7 取余 。

请你返回一个整数数组 answer,满足 answer.length == queries.length ,其中 answer[i]是第 i 个查询的结果。

示例 1:

输入:queries = [[2,6],[5,1],[73,660]]
输出:[4,1,50734910]
解释:每个查询之间彼此独立。
[2,6]:总共有 4 种方案得到长度为 2 且乘积为 6 的数组:[1,6],[2,3],[3,2],[6,1]。
[5,1]:总共有 1 种方案得到长度为 5 且乘积为 1 的数组:[1,1,1,1,1]。
[73,660]:总共有 1050734917 种方案得到长度为 73 且乘积为 660 的数组。1050734917 对 109 + 7 取余得到 50734910 。
示例 2 :

输入:queries = [[1,1],[2,2],[3,3],[4,4],[5,5]]
输出:[1,2,3,10,5]
 

提示:

1 <= queries.length <= 1e4
1 <= ni, ki <= 1e4

这个题的意思就是问给你一个m能分成多少个n个数的乘积

这个题不用担心没有结果哈,因为可以分成1的

我们知道s个球分到n个盒子里面的方案数为

C(n + s - 1, n - 1)
我们对m进行质因子分解为m=p1^a1*p2^a2---------
对于每一个p1可以看成把a1放在n个盒子中,最后对于不同的质因子有不同符合乘法原则
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e4+100;
class Solution {
public:
    int f[maxn],g[maxn];
    int qpow(int a,int b){
        int ans=1;
        while(b){
            if(b&1){
                ans=(ll)ans * a%mod;
            }
            a=(ll)a * a%mod;
            b>>=1;
        }
        return ans;
    }
    int C(int a,int b){
        return (ll)f[a] * g[b] % mod * g[a - b] % mod;
    }
    vector<int> waysToFillArray(vector<vector<int>>& queries) {
        f[0] = g[0] = 1;
        for (int i = 1; i < maxn; i ++ ) {
            f[i] = (ll)f[i - 1] * i % mod;
            g[i] = qpow(f[i], mod - 2);
        }
        vector<int>v;
        for(auto &q: queries){
            int n=q[0],m=q[1];
            int res=1;
            for (int i = 2; i * i <= m; i ++ ) {
                if (m % i == 0) {
                    int s = 0;
                    while (m % i == 0) s ++ , m /= i;
                    res = (ll)res * C(n + s - 1, n - 1) % mod;
                }
            }
            if (m > 1) res = (ll)res * C(n, n - 1) % mod;
            v.push_back(res);
        }
        return v;
    }
};
原文地址:https://www.cnblogs.com/lipu123/p/13946441.html