1 条题解
-
0
思路与算法
利用整数恒等式
本题 ,即使 很大,也只需计算分子和分母各 项的模积。因为 是素数且 ,分母 的每个因子都非零模 ,所以分母可逆。费马小定理给出
用二进制快速幂计算这个逆元,再与分子模积相乘。
不能直接套需要 的 模逆元模板,因为本题允许 。改用长度为 的连乘后,只有分母需要可逆;若分子包含 的倍数,结果自然为0。逐个处理 组查询。
正确性说明
组合数乘积恒等式在整数上成立,即 。对 取模后,因 且 素, 与 互质,可以在同余式两侧乘其逆元,得到程序使用的公式。费马小定理保证非零分母的 次幂就是逆元,包括 时指数0的边界。二进制快速幂正确计算该幂,逐步取模保持乘积余数,因此每组输出均为组合数的余数;证明不要求 。
复杂度
单组初始化为 ,两组连乘为 ,模幂为 ,总时间 ;全部查询为 。额外空间 ,不预处理到 或 ,也不构造巨大的精确组合数。
易错点
- 可逆条件是 且 素,不能把分子非零作为前提;分子为0时结果就是0。
- 时不要给 求逆。此题输入为 ,不是某些Lucas题中的 。
- 模数2、、 跨越模数、最大 均已覆盖。源称“正整数”,但模余数可以为0,不能输出 代替0。
- 模内乘积小于 ,使用64位并在每次乘法后取模。
- 独立答案以任意精度整数的精确组合数再取模获得,并与小规模Pascal三角核对;训练数据非官方原版。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; using ll = long long; ll power(ll a, ll b, ll mod) { ll answer = 1 % mod; while (b) { if (b & 1) answer = answer * a % mod; a = a * a % mod; b >>= 1; } return answer; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); ll tests; cin >> tests; while (tests--) { ll n, m, p; cin >> n >> m >> p; ll numerator = 1, denominator = 1; for (ll i = 1; i <= m; i++) { numerator = numerator * ((n - m + i) % p) % p; denominator = denominator * i % p; } cout << numerator * power(denominator, p - 2, p) % p << '\n'; } }
- 1