-
前言:
求一个组合数 ,我们可以通过逆元的方式在 O(n)的时间复杂度内求出
但如果数特别大时(数据范围 ),又该怎么办
使用卢卡斯定理求解
-
卢卡斯定理:(组合数取模,取模的模数只能是质数)
即
模板:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 100000 + 5;
LL mul(LL a, LL b, LL p){//快速乘,计算a*b%p
LL ret = 0;
while(b){
if(b & 1) ret = (ret + a) % p;
a = (a + a) % p;
b >>= 1;
}
return ret;
}
LL fact(int n, LL p){//n的阶乘求余p
LL ret = 1;
for (int i = 1; i <= n ; i ++) ret = ret * i % p ;
return ret ;
}
void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
if (!b) {d = a, x = 1, y = 0;}
else{
ex_gcd(b, a % b, y, x, d);
y -= x * (a / b);
}
}
LL inv(LL t, LL p){//如果不存在,返回-1
LL d, x, y;
ex_gcd(t, p, x, y, d);
return d == 1 ? (x % p + p) % p : -1;
}
LL comb(int n, int m, LL p){//C(n, m) % p
if (m < 0 || m > n) return 0;
return fact(n, p) * inv(fact(m, p), p) % p * inv(fact(n-m, p), p) % p;
}
LL Lucas(LL n, LL m, int p){
return m ? Lucas(n/p, m/p, p) * comb(n%p, m%p, p) % p : 1;
}
int main()
{
int T;
scanf("%d", &T);
while(T--){
LL n, m, p;
scanf("%lld%lld%lld", &n, &m, &p);
printf("%lld\n", Lucas(n, m, p)); //求C(n, m) % p
}
return 0;
}
-
扩展卢卡斯定理(可以处理模数为非质数的情况)
对于这样一个组合数:
(p不是质数)
例如:举这样一个例子,
if p = 6时,120 % 6 = 0,
6 = 2 * 3 (算术基本定理) 时,120 % 2 = 0,120 % 3 = 0,取一个同时满足条件的正整数为0,即
if p = 8时,120 % 98 = 22,
(算术基本定理)时,120 % 2 = 0,120 % 49 = 22,取一个同时满足条件的正整数为22, 即