给定n,m,求C(n,m)在膜1e18+7意义下的值。 每个测试点包括1e5组数据,从第1行至第1e5行每行两个数n,m。 输出1e5行,每行一个数为该组数据的答案。
时间: 2024-03-24 22:39:12 浏览: 104
由于模数很大,不能直接使用递推或求阶乘的方式来计算组合数。可以使用 Lucas 定理来计算组合数。
Lucas 定理是在模数为质数的情况下成立的。但是,对于模数为非质数的情况下,我们可以将模数分解质因数,然后对每个质因子使用 Lucas 定理来计算组合数,最后再使用中国剩余定理将所有结果合并即可。
以下是代码实现:
```c++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1e5;
const long long MOD1 = 1e18 + 7;
const vector<int> PRIMES = {2, 3, 5, 7, 11, 13, 17, 19};
const int PRIME_CNT = PRIMES.size();
// 求 a 的 b 次幂对 p 取模的结果
long long pow_mod(long long a, long long b, long long p) {
long long res = 1 % p;
while (b) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
// 求模数的逆元
long long inv(long long a, long long p) {
return pow_mod(a, p - 2, p);
}
// 预处理阶乘和阶乘的逆元
void init(vector<vector<long long>>& fac, vector<vector<long long>>& inv_fac, long long n) {
for (int i = 0; i < PRIME_CNT; i++) {
int p = PRIMES[i];
fac[i][0] = inv_fac[i][0] = 1;
for (int j = 1; j <= p; j++) {
if (j % p == 0) {
fac[i][j] = inv_fac[i][j] = 1;
} else {
fac[i][j] = fac[i][j - 1] * j % p;
inv_fac[i][j] = inv(fac[i][j], p);
}
}
}
}
// 计算组合数
long long C(long long n, long long m, int p, vector<vector<long long>>& fac, vector<vector<long long>>& inv_fac) {
if (m > n) return 0;
long long res = 1;
while (n || m) {
int a = n % p, b = m % p;
if (a < b) return 0;
res = res * fac[p][a] % p * inv_fac[p][b] % p * inv_fac[p][a - b] % p;
n /= p, m /= p;
}
return res;
}
int main() {
vector<vector<long long>> fac(PRIME_CNT, vector<long long>(MAXN + 1)), inv_fac(PRIME_CNT, vector<long long>(MAXN + 1));
init(fac, inv_fac, MAXN);
int T = 1e5;
while (T--) {
long long n, m;
cin >> n >> m;
long long res = 0;
for (int i = 0; i < PRIME_CNT; i++) {
int p = PRIMES[i];
res = (res + C(n, m, p, fac, inv_fac) * (MOD1 / p) % MOD1 * inv(MOD1 / p, p) % MOD1) % MOD1;
}
cout << res << endl;
}
return 0;
}
```
时间复杂度为 $O(T \log n \log p)$,其中 $T$ 表示测试点数,$n$ 表示组合数的上限,$p$ 表示模数的最大质因子。
阅读全文