传送门
逆元
什么是逆元?
首先取模运算在加法、减法、乘法中是满足分配率的,即
((a+b)\%p=(a\%p+b\%p)\%p)
((a*b)\%p=((a\%p)*(b\%p))\%p)
但是除法却不满足。
而有时候(frac{b}{a}\%p)中分子和分母都特别大,需要在计算过程中取模,这时候就需要把其转化成
(b*x\%p),其中x叫做a在模p意义下的逆元。即
(a*xequiv 1pmod p)
怎么求逆元?
-
费马小定理
当a、p互质时,
(a^{p-1}equiv1pmod p)
即 (a*a^{p-2}equiv1pmod p)
可以看出,此时(a^{p-2})即为a的逆元。
可以快速幂求出。 -
exgcd
直接根据同余方程(a*xequiv 1pmod p)用exgcd求解即可。 -
线性递推
用于求 1~n 在%p意义下的逆元。
设p=k*i+r,则(k*i+requiv0pmod p)
然后把等式两边同时乘i和r的逆元((x^{-1})表示x的逆元):
(k*i*i^{-1}*r^{-1}+r*i^{-1}*r^{-1}equiv0pmod p)
化简得:
(k*r^{-1}+i^{-1}equiv0pmod p)
再移项可得:
(i^{-1}equiv -k*r^{-1}pmod p)
把k替换成p/i,r替换成p%i,可以得出递推公式:
(inv[i]=-(p/i)*inv[p\%i]\%p)
注意逆元为正数,所以(inv[i]=(inv[i]+p)\%p)
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<ctime>
using namespace std;
int n,p,inv[3000005];
int main(){
cin>>n>>p;
inv[1]=1;
printf("1
");
for(int i=2;i<=n;i++) {
inv[i]=((-(1ll)*(p/i)*inv[p%i])%p+p)%p;
printf("%d
",inv[i]);
}
return 0;
}