跳转至

求组合数的方法

1.暴力

根据阶乘公式展开,时间复杂度较高,且容易炸掉long long

2.借用double暴力来求

http://icpc.upc.edu.cn/problem.php?cid=1438&pid=9

题目描述

Imagine you are attending your math lesson at school. Once again, you are bored because your teacher tells things that you already mastered years ago (this time he's explaining that (a+b)2=a2+2ab+b2). So you decide to waste your time with drawing modern art instead.

Fortunately you have a piece of squared paper and you choose a rectangle of size n*m on the paper. Let's call this rectangle together with the lines it contains a grid. Starting at the lower left corner of the grid, you move your pencil to the upper right corner, taking care that it stays on the lines and moves only to the right or up. The result is shown on the left: 在这里插入图片描述

Really a masterpiece, isn't it? Repeating the procedure one more time, you arrive with the picture shown on the right. Now you wonder: how many different works of art can you produce?

输入

The input contains several testcases. Each is specified by two unsigned 32-bit integers n and m, denoting the size of the rectangle. As you can observe, the number of lines of the corresponding grid is one more in each dimension. Input is terminated by n=m=0.

输出

For each test case output on a line the number of different art works that can be generated using the procedure described above. That is, how many paths are there on a grid where each step of the path consists of moving one unit to the right or one unit up? You may safely assume that this number fits into a 32-bit unsigned integer.

样例输入
5 4
1 1
0 0
样例输出

126
2
输入a和b,求C(b,a+b)

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    register ll n,k,a,b;
    register ll sum,p;
    register long double sum1=1.0;
    while(scanf("%lld%lld",&n,&k)&&(n!=0||k!=0))
    {
        sum1=1.0;
        a=n+k;
        b=n<k?n:k;
        for(int i=1;i<=b;i++)
        {
            sum1=sum1*a*1.0/i;
            a--;
        }
        sum1+=0.5;
        sum=sum1;
        printf("%lld\n",sum);
    }
    return 0;
}

3.逆元求组合数

http://icpc.upc.edu.cn/problem.php?id=12104

题目描述

You are given positive integers N and M. How many sequences a of length N consisting of positive integers satisfy a1×a2×...×aN=M? Find the count modulo 109+7. Here, two sequences a' and a" are considered different when there exists some i such that a'i≠a"i.

Constraints ·All values in input are integers. ·1≤N≤105 ·1≤M≤109

输入

Input is given from Standard Input in the following format: N M

输出

Print the number of the sequences consisting of positive integers that satisfy the condition, modulo 109+7.

样例输入

2 6

样例输出

4

提示

Four sequences satisfy the condition: {a1,a2}={1,6},{2,3},{3,2} and {6,1}.

思路

质因数分解,然后每个素数因子个数,设为x,转化为把x个相同的放进n个位置,开始写的n^x,但是wa,后来写的隔板法。 C(n+x-1,n-1)。 为啥不是n^x,因为元素相同,不同方案取决于每个盒子多少个球。又隔板法不能有空元素,但题目可以。所以我们添加三块隔板,答案为上。

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll N=1e9+7;
ll w[505500]={0};
ll p(ll x,ll y)
{
    ll sum1=1;
    while(y)
    {
        if (y&1)
            sum1=(sum1%N*x%N)%N;
        y>>=1;
        x=(x%N*x%N)%N;
    }
    return sum1%N;
}
ll c1(ll x,ll y)
{
    if(x<y)
        swap(x,y);
    return (w[x]%N*p((w[x-y]%N*w[y]%N)%N,N-2)%N)%N;
}
int main()
{
    ll n,m;
    w[1]=1;
    for(ll i=2;i<=500500;i++)
        w[i]=(w[i-1]*i)%N;
    cin>>n>>m;
    if(n==1)
    {
        cout<<1<<endl;
        return 0;
    }
    ll sum=1;
    for(ll i=2;i*i<=m+100;i++)
    {
        if(m%i!=0)
            continue;
        ll js=0;
        while(m%i==0)
        {
            m=m/i;
            js++;
        }
        sum=(sum%N*c1(n-1,n+js-1)%N)%N;
    }
    if(m!=1)
        sum=(sum%N*n%N)%N;
    cout<<sum%N<<endl;
    return 0;
}