[Luogu3807] 【模板】卢卡斯定理 [Lucas定理]

本文探讨了Lucas定理在组合数学中求解C(n, m) % p问题的应用,其中p为质数。介绍了如何通过预处理实现O(p)的时间复杂度,并在单次查询中达到O(log p)的效率。此外,还详细讲解了线性求逆元的方法,包括A[i]的计算公式及其数学证明。

Link
Luogu - https://www.luogu.org/problemnew/show/P3807


n , m n,m n,m 为正整数, p p p 为质数 那么
( n m ) % p = [ ( n % p m % p ) % p ] ∗ [ ( n / p m / p ) % p ] % p \binom{n}{m}\%p=\left[\binom{n\%p}{m\%p}\%p\right]*\left[\binom{n/p}{m/p}\%p\right]\%p (mn)%p=[(m%pn%p)%p][(m/pn/p)%p]%p
时间复杂度如果算上预处理是 O ( p ) O(p) O(p)
单次查询 O ( log ⁡ p ) O(\log_p) O(logp) 左右?


另外一个常用的东西
线性求逆元
A[i] = (p - p / i) * A[p % i];
证明参考
p = k i + r p=ki+r p=ki+r
k i + r ≡ 0 ( m o d p ) ki+r\equiv0\pmod{p} ki+r0(modp)
k r − 1 ≡ − i − 1 ( m o d p ) kr^{-1}\equiv-i^{-1}\pmod{p} kr1i1(modp)
i − 1 ≡ − ⌊ p i ⌋ ⋅ r − 1 ( m o d p ) i^{-1}\equiv-\lfloor\frac{p}{i}\rfloor\cdot r^{-1}\pmod{p} i1ipr1(modp)


#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<vector>
using namespace std;
const int MAXN = 1e5+5;
int T, n, m, p, stair[MAXN], rev[MAXN], inv[MAXN];
inline int C(const int& n, const int& m)
{
	if (m > n) return 0;
	return 1ll * stair[n] * rev[m] % p * rev[n-m] % p;
}
int Lucas(const int& n, const int& m, const int& p)
{
	if (!m) return 1;
	return 1ll * Lucas(n/p, m/p, p) * C(n%p, m%p) % p;
}
int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &n, &m, &p);
		inv[0] = 0;
		stair[0] = stair[1] = inv[1] = rev[0] = rev[1] = 1;
		for (register int i = 2; i < p; ++i) stair[i] = 1ll * stair[i-1] * i % p;
		for (register int i = 2; i < p; ++i) inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
		for (register int i = 2; i < p; ++i) rev[i] = 1ll * rev[i-1] * inv[i] % p;
		printf("%d\n", Lucas(n + m, m, p));
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值