还是逆元之O(n)阶乘逆元。。。

本文分享了一种高效计算阶乘逆元的方法,利用快速幂求最末项逆元,再递归计算整个序列,提高了组合数计算的速度。

除草

做一个题发现了一个逆元的知识盲点,就是阶乘的逆元

然后发现了可以这样

fac[0]=fac[1]=1;

for(int i=2;i<=MAXN;i++)fac[i]=fac[i-1]*i%mod;

inv[MAXN]=quipow(fac[MAXN],mod-2);

for(int i=MAXN-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;

发现这个以后惊呆了

就是那种这明明没有什么特别难理解的道理可是就是忘了那些最基本的道理的感觉

继续努力

### 逆元预处理算法的实现方法 在编程中,若需频繁计算 `1, 2, ..., n` 模 `p` 的逆元,可以使用 **O(n)** 时间复杂度的逆元预处理算法。该方法基于以下递推公式: $$ \text{inv}[i] = (p - p / i) \times \text{inv}[p \% i] \% p $$ 其中 `p` 是一个质数,且 `inv[1] = 1`。通过这个公式,可以递推计算出 `1` 到 `n` 的所有逆元。 #### 算法实现步骤 1. 初始化 `inv[0] = 1`(虽然 `inv[0]` 无实际意义,但为了方便计算)。 2. 设置 `inv[1] = 1`,因为 `1` 的逆元是它本身。 3. 使用上述递推公式,从 `2` 到 `n` 依次计算每个数的逆元。 #### 示例代码 ```cpp #include <bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; // 假设模数为一个大质数 const int MAXN = 1e5 + 10; // 最大需要计算的逆元数量 int inv[MAXN]; // 存储逆元 void precompute_inv(int n) { inv[1] = 1; // 1的逆元是1 for (int i = 2; i <= n; ++i) { inv[i] = (long long)(MOD - MOD / i) * inv[MOD % i] % MOD; } } int main() { int n = 10; // 假设需要计算1~10的逆元 precompute_inv(n); for (int i = 1; i <= n; ++i) { cout << "inv[" << i << "] = " << inv[i] << endl; } return 0; } ``` #### 代码说明 - `MOD` 是模数,通常是一个大质数。 - `precompute_inv(n)` 函数用于预处理 `1` 到 `n` 的逆元。 - `inv[i]` 表示 `i` 的逆元。 #### 算法复杂度分析 - **时间复杂度**:`O(n)`,因为每个逆元的计算只需要一次递推。 - **空间复杂度**:`O(n)`,用于存储 `1` 到 `n` 的逆元。 #### 应用场景 - **组合数计算**:在需要频繁计算组合数 `C(n, k)` 时,可以通过预处理阶乘阶乘逆元,快速计算出结果[^4]。 - **模运算优化**:在需要多次进行模运算的场景中,预先计算好逆元可以显著提升性能。 #### 注意事项 - 该算法要求模数 `p` 必须是质数。 - 确保 `i` 与 `p` 互质,否则逆元不存在。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值