洛谷 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*xequiv 1pmod p)

怎么求逆元?

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

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

  3. 线性递推
    用于求 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;
}
原文地址:https://www.cnblogs.com/yinyuqin/p/14773781.html