Loading

卢卡斯定理

( 不是卢斯卡...

求组合数模数的方法

知乎-算法学习笔记(25): 卢卡斯定理

\[C_m^n = \dfrac {m!} {n!(m-n)!} \]

一般情况下对一个大素数 \(p\) 取模, 可以线性处理出阶乘,阶乘的逆元, \(O(1)\) 计算就可以。

const int N = 1e6 + 10;
int fac[N],invfac[N],inv[N];
const int mod = 998244353;

void init(){
    fac[0] = invfac[0] = 1;
    fac[1] = invfac[1] = 1;
    inv[1] = 1;
    for(int i = 2;i < N;i++){
        fac[i] = fac[i-1] * i % mod;
        inv[i] = (mod - mod / i)*inv[mod % i] % mod;
        invfac[i] = invfac[i-1] * inv[i] % mod;
    }
}

int C(int n,int m){
    return fac[n]*invfac[n-m]*invfac[m];
}

但是, 当 \(p < m\) 的时候,就需要用 \(Lucas\)

\[C_m^n = C_{m\%p}^{n\%p} \cdot \ C_{m/p}^{n/p} \ \ (mod\ \ p) \]

【卢卡斯模板】

/*
 * @Author: zhl
 * @Date: 2020-11-12 10:27:57
 */

#include<bits/stdc++.h>
using ll = long long;
using namespace std;

const int N = 1e6 + 10;
ll fac[N], invfac[N], inv[N];


void init(int n, int mod) {
	fac[0] = invfac[0] = 1;
	fac[1] = invfac[1] = 1;
	inv[1] = 1;
	for (int i = 2; i < n; i++) {
		fac[i] = fac[i - 1] * i % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
		invfac[i] = invfac[i - 1] * inv[i] % mod;
	}
}


// 需要先预处理出fact[],即阶乘
ll C(ll m, ll n, ll p){
	return m < n ? 0 : fac[m] * invfac[n] % p * invfac[m - n] % p;
}
ll lucas(ll m, ll n, ll p){
	return n == 0 ? 1 % p : lucas(m / p, n / p, p) * C(m % p, n % p, p) % p;
}

int T, n, m, k;
int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d%d", &n, &m, &k);
		init(n + m + 1, k);
		printf("%lld\n", lucas(n + m, n, k));
	}
}
posted @ 2020-11-12 19:45  —O0oO-  阅读(134)  评论(0编辑  收藏  举报