数学-求组合数 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;
}


posted @ 2022-07-26 20:17  lucky_light  阅读(60)  评论(0编辑  收藏  举报