数学-求组合数 III - 卢卡斯定理
c++
AcWing 887. 求组合数 III
/*
* 题目描述:
* AcWing 887. 求组合数 III
* 给定 n 组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 C(a, b) mod p 的值。
* 输入格式:
* 第一行包含整数 n。
* 接下来 n 行,每行包含一组 a,b,p。
* 输出格式:
* 共 n 行,每行输出一个询问的解。
* 数据范围:
* 1 ≤ n ≤ 20,
* 1 ≤ b ≤ a ≤ 10 ^ 18,
* 1 ≤ p ≤ 10 ^ 5,
* 数据范围
* 1 ≤ n, ai ≤ 100
* 解题思路:
* 本题主要难点在于卢卡斯定理,剩余部分主要靠逆元来求解。
* 卢卡斯定理:
* C(a, b) % p = C(a / p, b / p) * C(a % p, b % p) % p
* 其中 p 是质数,看是卢卡斯定理只是一个式子,但是等式右侧第一项是可以递归的。
* 并且经过分析可知,为 log p 深度,不难感知到 Lucas 定理对算法复杂度的优化。
*
* 证明 Lucas Theorem:
* 首先,证明 C(p, i) % p = 0, 其中 i ∈ [1, p - 1]。
* 这是因为 p 作为分子,又是质数,无法被消去,因此 C(p, i) 当 i ∈ [1, p - 1] 是 p 倍数。
* 然后,可知
* (1 + x) ^ (sp + q) % p = ((1 + x) ^ p) ^ s * (1 + x) ^ q % p
* = (1 + x ^ p) ^ s * (1 + x) ^ q % p
* x ^ (tp + r) 的系数等式左侧为 C(sp + q, tp + r)
* 等式右侧为 C(s, t) * C(q, r)
* C(sp + q, tp + r) % p = C(s, t) * C(q, r) % p
* 证明完毕。
*
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
typedef long long LL;
int n;
LL a, b, p;
LL qmi(LL a, LL b, LL c) {
LL res = 1;
while (b) {
if (b & 1) {
res = res * a % c;
}
b >>= 1;
a = a * a % p;
}
return res;
}
LL get_reverse(LL i, LL p) {
return qmi(i, p - 2, p);
}
LL get_c(LL u, LL v, LL p) {
LL res = 1;
for (int i = u; i >= u - v + 1; i -- ) {
res = res * i % p;
}
for (int i = 2; i <= v; i ++ ) {
res = res * get_reverse(i, p) % p;
}
return res;
}
LL get_result(LL a, LL b, LL p) {
LL u, v;
int res = 1;
while (a > 0) {
u = a % p, v = b % p;
a /= p, b /= p;
res = res * get_c(u, v, p) % p;
}
return res;
}
int main()
{
scanf("%d", &n);
while (n -- ) {
scanf("%lld%lld%lld", &a, &b, &p);
printf("%lld\n", get_result(a, b, p));
}
return 0;
}