AcWing 886:求组合数 II ← 费马小定理 + 快速冥 + 阶乘逆元

【题目来源】
https://www.acwing.com/problem/content/888/

【题目描述】
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b) mod (10^9+7) 的值。

【输入格式】
第一行包含整数 n。
接下来 n 行,每行包含一组 a 和 b。

【输出格式】
共 n 行,每行输出一个询问的解。

【输入样例】
3
3 1
5 3
2 2

【输出样例】
3
10
1

【数据范围】
1≤n≤10000,
1≤b≤a≤10^5

【算法分析】
● 本题与“AcWing 885:求组合数 I”(https://blog.csdn.net/hnjzsyjyj/article/details/156116093)的区别仅仅在于 a、b 的数据规模不同,达到了 10^5。

● C(n, m) 在数学中通常表示‌组合数‌,即从 n 个不同元素中选取 m 个元素的组合数目,计算公式为:n! / (m! * (n-m)!)。

● 组合数在“数位DP”中经常用到。

● 阶乘逆元的计算核心是‌费马小定理‌。当模数 p 为质数时,费马小定理给出 a^(p-1) ≡ 1 (mod p),从而 a × a^(p-2) ≡ 1 (mod p),表明 a 的逆元为 a^(p-2) mod p。‌

● 快速幂是计算 a^(p-2) mod p 的高效工具,定义为 fastPow(a, p-2, p)。它将逆元求解的时间复杂度降至 O(log p)。‌

int fastPow(LL a,LL n,LL p) {
    LL ans=1;
    while(n) {
        if(n & 1) ans=ans*a%p;
        n>>=1;
        a=a*a%p;
    }
    return ans%p;
}

快速幂求逆元仅适用于模数 p 为质数的情况

● 代码中 MOD=1e9+7 是常用质数。

● 定义 fac[i]=i! % p,invfac[i]=(i!)^(-1) % p。其中,p 为质数。
已知 1/i! = 1/(i-1)! * 1/i,而 1/i 为 i 的逆元,利用费马小定理知 i 的逆元等于 i^(p-2) mod p。综上可得,invfac[i] = invfac[i-1] * fastPow(i, p-2, p)


【算法代码】

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

typedef long long LL;
const int MOD=1e9+7;
const int N=1e5+5;
LL fac[N],invfac[N];

int fastPow(LL a,LL n,LL p) {
    LL ans=1;
    while(n) {
        if(n & 1) ans=ans*a%p;
        n>>=1;
        a=a*a%p;
    }
    return ans%p;
}

int main() {
    fac[0]=invfac[0]=1;
    for(int i=1; i<N; i++) {
        fac[i]=fac[i-1]*i%MOD;
        invfac[i]=(LL)invfac[i-1]*fastPow(i,MOD-2,MOD)%MOD;
    }

    int n;
    cin>>n;
    while(n--) {
        int a,b;
        cin>>a>>b;
        cout<<(LL)fac[a] * invfac[b]%MOD * invfac[a-b]%MOD<<endl;
    }
}

/*
in:
3
3 1
5 3
2 2

out:
3
10
1
*/





【参考文献】
https://www.acwing.com/solution/content/22076/
https://www.acwing.com/blog/content/406/
https://www.acwing.com/solution/content/87578/
 

posted @ 2025-12-22 09:21  Triwa  阅读(0)  评论(0)    收藏  举报