AcWing 886. 求组合数 II

208 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第45天,点击查看活动详情

AcWing 886. 求组合数 II

给定 n 组询问,每组询问给定两个整数 a,b,请你输出 Ca^b mod (10^9+7) 的值。

输入格式

第一行包含整数 n。

接下来 n 行,每行包含一组 a 和 b。

输出格式

共 n 行,每行输出一个询问的解。

数据范围

1≤n≤10000,
1≤b≤a≤10^5

输入样例:

3
3 1
5 3
2 2

输出样例:

3
10
1

思路

若在mod p意义下,对于一个整数a,有a*b≡1(mod p),那么这个整数b即为a的乘法逆元,同时a也为b的乘法逆元,这里的 b 对于 a 来说类似于 1/a 的存在。

求 (a / b)%p 等同于求 a∗(b的逆元)%p,除以一个数等同于乘一个数的倒数。

有什么用呢?对于一个分数,分子分母同余一个数是不等于原分数模这个数的,而两个数相乘的时候是可以的。

即 (a / b) % p ≠ ((a % p ) / ( b % p)) % p,而 (a * b) % p = ((a % p ) * ( b % p)) % p

需要取模运算的时候,乘法是优于除法的,所以有了逆元的概念。 Snipaste_2022-07-25_19-55-36.png

image.png

通过预处理逆元的方式求组合数模板

首先预处理出所有阶乘取模的余数fact[N],以及所有阶乘取模的逆元infact[N]
如果取模的数是质数,可以用费马小定理求逆元
int qmi(int a, int k, int p){   // 快速幂模板
    int res = 1;
    while (k){
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}
// 预处理阶乘的余数和阶乘逆元的余数
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ ){
    fact[i] = (LL)fact[i - 1] * i % mod;
    infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}

ac代码

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int fact[N], infact[N];
int qmi(int a, int k, int p){
    int res = 1;
    while (k){
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}
int main(){
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++ ){
        fact[i] = (LL)fact[i - 1] * i % mod;
        infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
    int n;
    scanf("%d", &n);
    while (n -- ){
        int a, b;
        scanf("%d%d", &a, &b);
        printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
    }
    return 0;
}