难度
普
及
/
提
高
\color{yellow}普及/提高
普及/提高
板子题,给出两种方法
题意
给
n
,
p
n,p
n,p
求出所有
i
∈
[
1
,
n
]
i\in[1,n]
i∈[1,n]在模
p
p
p 情况下的逆元
i
−
1
i^{-1}
i−1
一个一个求肯定是 O ( N log N ) O(N\log N) O(NlogN),容易超。这里给出的两种都为 O ( N ) O(N) O(N)的。
数据范围
n
≤
3
×
1
0
6
n\le 3\times10^6
n≤3×106
n
<
p
<
20000528
n<p<20000528
n<p<20000528 且
p
p
p 是质数
方法1:阶乘逆元法
- 我们可以
O
(
N
)
O(N)
O(N) 预处理出所有
(
i
!
)
−
1
(i!)^{-1}
(i!)−1,方法如下:
- 首先算出 n ! n! n! ,可以直接递推获得。
- 再算出 ( n ! ) − 1 (n!)^{-1} (n!)−1,使用费马小定理即可,即 ( n ! ) − 1 ≡ ( n ! ) p − 2 ( m o d p ) (n!)^{-1}\equiv(n!)^{p-2}\pmod{p} (n!)−1≡(n!)p−2(modp)
- 接下来,递推 ( i ! ) − 1 = ( ( i + 1 ) × ( i + 1 ) ! − 1 ) % p (i!)^{-1}=((i+1)\times(i+1)!^{-1})\% p (i!)−1=((i+1)×(i+1)!−1)%p 即可。
- 计算 ( i ) − 1 = ( i ! ) − 1 × ( i − 1 ) ! % p (i)^{-1}=(i!)^{-1}\times (i-1)!\%p (i)−1=(i!)−1×(i−1)!%p 即可。
方法2:线性求逆元
- 我们求模 p p p 下 i i i 的逆元,我们使用带余除法写出表达式子:
- p = i × k + r p=i\times k+r p=i×k+r,其中 0 < r < i 0<r<i 0<r<i
- 其中 k = p i k=\frac{p}{i} k=ip为商, r = p % i r=p\%i r=p%i为余数。
- 接下来我们写出同余等式的表达式子:
- i × k + r ≡ 0 ( m o d p ) i\times k+r\equiv 0\pmod{p} i×k+r≡0(modp)
- 两边同时除以 r × i r\times i r×i,是为了得到 i − 1 i^{-1} i−1这一项在单独的一边。我们得到:
- r − 1 × k + i − 1 ≡ 0 ( m o d p ) r^{-1}\times k+i^{-1}\equiv 0\pmod{p} r−1×k+i−1≡0(modp) 移项可得:
- i − 1 ≡ − r − 1 × k ( m o d p ) i^{-1}\equiv -\ r^{-1}\times k\pmod{p} i−1≡− r−1×k(modp) 我们把 r r r 和 k k k 带入可得:
-
i
−
1
≡
−
(
p
%
i
)
−
1
×
p
i
(
m
o
d
p
)
i^{-1}\equiv -\ (p\%i)^{-1}\times \frac{p}{i}\pmod{p}
i−1≡− (p%i)−1×ip(modp)
接下来写成代码即可。
注意一下,由于里面有负号,取模意义下不会出现负号,负数取模完加上模数再取模即可。
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
const int MAX = 3e6+50;
const ll MOD = 19260817;
ll Inv[MAX];
int main()
{
int n,p;
scanf("%d%d",&n,&p);
Inv[1] = 1;
puts("1");
for(int i = 2;i <= n;++i){
Inv[i]=(p-(p/i)*Inv[p%i]%p)%p;
printf("%lld\n",Inv[i]);
}
return 0;
}