低数量级
1.利用杨辉三角直接求。C(n,m)=C(n-1,m)+C(n-1,m-1)。
1)递归结束条件:如果m=n或n=0递归返回1:
if(n == 0 || m == n)
return 1;
2)代码实现:
#include <iostream>
using namespace std;
int long long C(int m,int n)
{
if(n == 0 || m == n)
return 1;
else
{
return C(m-1,n-1)+C(m-1,n);
}
}
int main()
{
int m,n;
cin>>m>>n;
cout<<C(m,n)<<endl;
return 0;
}
2.利用定义直接求:
代码实现:
int long long C(int a, int b)
{
int long long ans = 1;
for (int i = a; i > a - b; i--) ans *= i;
for (int i = b; i > 1; i--) ans /= i;
return ans;
}
高数量级(取模运算)用快速幂的方法求组合数
需要用到的知识
-
拓展:
n>>=1 右移1位 除以2;n<<=1 左移1位 乘以2;
有关位运算
-
费马小定理(有关费马小定理)
费马小定理:假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)
所以a*a^(p-2)≡1(mod p)
得出:a^(p-2)就是a的乘法逆元(有关乘法逆元)
利用快速幂可以计算出a^(p-2) - 快速幂
long long quickpow(long long a,long long b,long long p) //快速幂
{
if(b<0)
return 0;
long long ret=1;
a%=p; // 例如:29^25=7^25 %11
while(b)
{
if(b & 1 )
ret = ( (ret % p) * (a% p) ) % p
b>>=1;
a = ((a% p) * (a% p))% p;
}
return ret;
}
- Lucas定理
Lucas定理是用来求 c(n,m) mod p,p为素数的值。
C(n,m)%p = C( n/p , m/p ) * C( n%p , m%p ) %p
也就是Lucas(n,m)%p = Lucas(n/p,m/p) * C( n%p , m%p ) %p
求上式的时候,Lucas递归出口为m=0时返回1
求C(n%p, m%p)%p的时候,此处写成C(n, m)%p(p是素数,n和m均小于p)
C(n, m)%p = n! / (m ! * (n - m )!) % p = n! * mod_inverse[m! * (n - m)!, p] % p
由于p是素数,有费马小定理可知,m! * (n - m)! 关于p的逆元(mod_inverse)就是m! * (n - m)!的p-2次方。
代码实现( Lucas定理):
long long C(int n, int m, long long p)//组合数C(n, m) % p
{
if(m > n)return 0;
long long up = 1, down = 1;// 分子 分母;
for(int i = n - m + 1; i <= n; i++)up = up * i % p;
for(int i = 1; i <= m; i++)down = down * i % p;
return up * inv(down, p) % p;
}
long long Lucas(long long n, long long m, long long p)
{
if(m == 0)return 1;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}
求组合数代码实现:
#include <bits/stdc++.h>
using namespace std;
const int p=1e9 + 7;
typedef unsigned long long ll;
ll qpow(ll n,ll k)
{
ll res=1;
while(k){
if(k&1) res=(res%p)*(n%p)%p;
n=(n%p)*(n%p)%p;
k>>=1;
}
return res;
}
ll C(ll n,ll k)
{
if(n<k) return 0;
ll res=1;
for(int i=1;i<=k;i++){
ll a=(n-k+i)%p;
ll b=i%p;
res=res*(a*qpow(b,p-2)%p)%p;
}
return res%p;
}
ll lucas(ll n,ll k)
{
if(k==0) return 1;
return (C(n%p,k%p)%p)*(lucas(n/p,k/p)%p)%p;
}
int main()
{
ll m,n;
cin>>m,n;
cout<<lucas(m,n)<<endl;
return 0;
}