🎈组合数
一、公式递推法(懒人必备法)
时间复杂度 O(n^2) 空间复杂度 O(n^2).
适用于 “小规模”、“小范围” 的求法
公式:C(a,b) = C(a-1,b) + C(a-1,b-1)
for(int i = 0; i <= n; ++ i)
for(int j = 0; j <= i; ++ j)
if(!j) f[i][j] = 1;
else f[i][j] = f[i-1][j-1]+f[i][j-1];
二、预处理法
时间复杂度 O(nlogp) 空间复杂度 O(n).
适用于 “中小规模” 、”中小范围” 的求法
公式:C(a,b) = a! / ( b! * (a-b)! ) = a! * b!^-1 * (a-b)!^-1
⭐ 一般情况下,当模的数p为质数时,根据费马小定理求逆元。
费马小定理: p^a-1 ≡ 1( mod p )
typedef long long ll;
int fact[N],infact[N];
//定义
int inv(int a,int x,int p)//快速幂求逆元
{
int res = 1;
while(x)
{
if(x&1) res = (ll) res * a % p;
a = (ll) a*a%p;//(ll)
x>>=1;
}
return res;
}
int main()
{
fact[0] = infact[0] = 1;
for(int i = 1;i<N;i++)
{
fact[i] = (ll) fact[i-1]*i%p;
infact[i] = (ll) infact[i-1]*inv(i,p-2,p)%p;
}//预处理阶乘值,和逆元。
printf("%d\n",(ll) fact[a]*infact[b]%p*infact[a-b]%p);//输出结果
}
三、Lucas定理
处理大概 p 在 1e5 内的数据量 a,b不限量,但是只能计算20组左右的 数据
适用于 大范围,小个数 的求法
⭐ 本解法用Lucas公式,缩短了所求的范围,然后用预处理的方法求解。
#include <iostream>
using namespace std;
typedef long long ll;
int p;
int fp(int a,int x)//快速幂
{
int res = 1;
while(x)
{
if(x & 1) res = (ll) res * a % p;
a =(ll) a*a%p;
x>>=1;
}
return res;
}
int C(int a,int b)//预处理阶乘,逆元。
{
int res = 1;
for(int i = 1,j = a;i<=b;i++,j--)
{
res = (ll) res * j % p;
res = (ll) res * fp(i,p-2) % p;
}
return res;
}
int lucas(ll a,ll b)//Lucas定理
{
if(a<p && b<p) return C(a,b);//第一种情况比p小,所以直接输出。
return (ll) C(a%p,b%p) * lucas(a/p,b/p) %p;
}
int main()
{
ll a,b;
cin>> a >> b >> p;
cout<<lucas(a,b)<<endl;
}
公式原理法
C(a,b) 利用手推的公式来进行,模拟手推计算。适合于b比较小的情况。
第一个公式写错了,应该是C[a,b] = C[a-1,b] + C[a-1,b-1]
就是看不懂,再解释一下就好了
真牛逼
大佬Orz
大佬咱能换句话吗?