O(n)递(逆)推求乘法逆元
题目链接
题目描述
给定 \(n,p\), 求 \(1\sim n\) 中所有整数在模 \(p\) 意义下的乘法逆元。
输入格式
一行两个正整数 \(n,p\)。
输出格式
输出 \(n\) 行,第 \(i\) 行表示 \(i\) 在模 \(p\) 下的乘法逆元。
输入
10 13
输出
1
7
9
10
8
11
2
5
3
4
说明/提示
\(1 \leq n \leq 3 \times 10 ^ 6, n < p < 20000528\)
输入保证 \(p\) 为质数。
解题思路
线性求逆元
- \(1^{−1}≡1(mod\,p)\)
- 设 \(p=k\times i +r,(1<r<i<p)\), \(k\) 是 \(p / i\) 的商,\(r\) 是余数,则:
\[k\times i+r≡0(mod\,p)
\]
然后乘上\(i^{-1} ,r^{-1}\) 就可以得到:
\[k*r^{-1}+i^{-1}\equiv 0 \pmod p
\]
\[i^{-1}\equiv -k*r^{-1} \pmod p
\]
\[i^{-1}\equiv -\lfloor \frac{p}{i} \rfloor*(p \bmod i)^{-1} \pmod p
\]
可以发现,这是个递推式~
- 时间复杂度:\(O(n)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e6+10;
int inv[N];
int n,p;
int main()
{
scanf("%d%d",&n,&p);
inv[1]=1;
puts("1");
for(int i=2;i<=n;i++)
{
inv[i]=1ll*(-p/i+p)*inv[p%i]%p;
printf("%d\n",inv[i]);
}
return 0;
}
逆推求阶乘逆元
假设我们已经求出 \(n!\) 的逆元 \((n!)^{-1}\),现在我们要求 \((n-1)!\) 的逆元,而:
\[n! \times (n!)^{-1} \equiv1 (\bmod p) \Longleftrightarrow (n-1)! \times n (n!)^{-1} \equiv1 (\bmod p)
\]
即 \((n-1)!\) 的逆元为 \(n (n!)^{-1}\),反过来,\(n\) 的逆元为 \((n-1)! \times (n!)^{-1}\)
- 时间复杂度:\(O(n)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e6+10;
int n,p;
int fact[N],inv_fact[N];
int ksm(int a,int b)
{
int res=1%p;
for(;b;b>>=1)
{
if(b&1)res=1ll*res*a%p;
a=1ll*a*a%p;
}
return res;
}
int main()
{
scanf("%d%d",&n,&p);
fact[0]=1;
for(int i=1;i<=n;i++)fact[i]=1ll*fact[i-1]*i%p;
inv_fact[n]=ksm(fact[n],p-2);
for(int i=n-1;i;i--)
inv_fact[i]=1ll*(i+1)*inv_fact[i+1]%p;
for(int i=1;i<=n;i++)
printf("%d\n",int(1ll*fact[i-1]*inv_fact[i]%p));
return 0;
}