洛谷 P3811 【模板】乘法逆元

传送门


逆元

什么是逆元?

首先取模运算在加法、减法、乘法中是满足分配率的,即
\((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*x\equiv 1\pmod p\)

怎么求逆元?

  1. 费马小定理
    当a、p互质时,
      \(a^{p-1}\equiv1\pmod p\)
    即 \(a*a^{p-2}\equiv1\pmod p\)
    可以看出,此时\(a^{p-2}\)即为a的逆元。
    可以快速幂求出。

  2. exgcd
    直接根据同余方程\(a*x\equiv 1\pmod p\)exgcd求解即可。

  3. 线性递推
    用于求 1~n 在%p意义下的逆元。
    设p=k*i+r,则\(k*i+r\equiv0\pmod p\)
    然后把等式两边同时乘i和r的逆元(\(x^{-1}\)表示x的逆元):
    \(k*i*i^{-1}*r^{-1}+r*i^{-1}*r^{-1}\equiv0\pmod p\)
    化简得:
    \(k*r^{-1}+i^{-1}\equiv0\pmod 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\n");
	for(int i=2;i<=n;i++) {
		inv[i]=((-(1ll)*(p/i)*inv[p%i])%p+p)%p;
		printf("%d\n",inv[i]);
	}
	return 0;
}
posted @ 2021-05-16 15:06  尹昱钦  阅读(187)  评论(0)    收藏  举报