题目
这是一道模板题
题目描述
给定 n , p 求 1 ∼ n n,p求 1\sim n n,p求1∼n中所有整数在模 p p p 意义下的乘法逆元。
输入格式
一行两个正整数 n , p n,p n,p。
输出格式
输出 n n n 行,第 i i i 行表示 i i i 在模 p p p 下的乘法逆元。
输入输出样例
输入
10 13
输出
1
7
9
10
8
11
2
5
3
4
说明/提示
1 ≤ n ≤ 3 × 1 0 6 , n < p < 20000528 1 \leq n \leq 3 \times 10 ^ 6, n < p < 20000528 1≤n≤3×106,n<p<20000528
输入保证 p p p 为质数。
题解
模板乘法逆元
可把刚打好的扩欧乘法逆元板子交上去结果发现 T 了,
看了眼题解才发现要用线性方法求逆元,因为题目要求1~n的逆元。
线性法要推导一个递推公式:
设
p
=
k
∗
i
+
r
p=k*i+r
p=k∗i+r
也就是说
p
/
i
p/i
p/i 的商为
k
k
k,余数为
r
r
r。
∴
k
=
⌊
p
i
⌋
,
r
=
p
m
o
d
i
\therefore k=⌊ \frac{p}{i} ⌋, \ r=p\; mod\; i
∴k=⌊ip⌋, r=pmodi
得 k ∗ i + r ≡ 0 ( m o d p ) k*i+r\equiv0\ \ (mod\ \ p) k∗i+r≡0 (mod p)
左右同乘 ( i − 1 ∗ r − 1 ) (i^{-1}*r^{-1}) (i−1∗r−1), \;\;\;\;\;\;\; // x − 1 x^{-1} x−1 表示 x 的逆元
得: k ∗ r − 1 + i − 1 ≡ 0 ( m o d p ) k*r^{-1}+i^{-1}\equiv0\ \ (mod\ \ p) k∗r−1+i−1≡0 (mod p)
代入 k = ⌊ p i ⌋ , r = p m o d i k=⌊\frac{p}{i}⌋, \ r=p\; mod\; i k=⌊ip⌋, r=pmodi
得: ⌊ p i ⌋ ∗ ( p m o d i ) − 1 + i − 1 ≡ 0 ( m o d p ) ⌊ \frac{p}{i}⌋*(p\; mod\; i)^{-1}+i^{-1}\equiv0\ \ (mod\ \ p) ⌊ip⌋∗(pmodi)−1+i−1≡0 (mod p)
∴ i − 1 ≡ − ⌊ p i ⌋ ∗ ( p m o d i ) − 1 ( m o d p ) \therefore i^{-1}\equiv-⌊ \frac{p}{i}⌋*(p\; mod\; i)^{-1}\ \ (mod\ \ p) ∴i−1≡−⌊ip⌋∗(pmodi)−1 (mod p)
∴ i − 1 % p = − ⌊ p i ⌋ ∗ ( p % i ) − 1 % p \therefore i^{-1}\;\%\;p=-⌊ \frac{p}{i}⌋*(p\;\%\; i)^{-1}\;\%\;p ∴i−1%p=−⌊ip⌋∗(p%i)−1%p
∴ i − 1 = − ⌊ p i ⌋ ∗ ( p % i ) − 1 % p \therefore i^{-1}=-⌊ \frac{p}{i}⌋*(p\;\%\; i)^{-1}\;\%\;p\;\;\;\;\; ∴i−1=−⌊ip⌋∗(p%i)−1%p ( i 的逆元必小于 p,所以左边模 p可省掉)
所以我们便有了个递推式:
i n v [ i ] = − p / i ∗ i n v [ p % i ] % p inv[i]=-p/i*inv[p\ \%\;i]\;\%\; p inv[i]=−p/i∗inv[p %i]%p
∵ p % p 等 于 0 \because p\%p\;等于0 ∵p%p等于0
∴ i n v [ i ] = p − p / i ∗ i n v [ p % i ] % p 加 上 p 答 案 不 变 \therefore inv[i]=p-p/i*inv[p\ \%\;i]\;\%\; p\;\;\;\;\;\;\;\;\;\;\;\;加上p答案不变 ∴inv[i]=p−p/i∗inv[p %i]%p加上p答案不变
这样就能保证 i n v [ i ] > 0 inv[i]>0 inv[i]>0
超级简洁的代码
#include<iostream>
using namespace std;
long long p,n,inv[10000001];
int main()
{
scanf("%d%d",&n,&p);
inv[1]=1; //初始化,1在所有意义下的逆元都为1
for(int i=2; i<=n; i++) inv[i]=(-p/i)*inv[p%i]%p; //从2开始套式子递推
for(int i=1; i<=n; i++) printf("%d\n",inv[i]); //最后输出
return 0;
}