[模板]乘法逆元

Posted risinggods

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[模板]乘法逆元相关的知识,希望对你有一定的参考价值。

本博客所有代码基于题目 luogu_P3811

逆元:

  一般用于求 (a/b) mod p 

定义:

  若 a*≡ (mod p) ,且 a 与 p 互质,那么我们就能定义: 为 的逆元,记为 a^-1 ,所以我们也可以称 x 为 a 的倒数(mod p意义下)。

  所以对于 (a/b) mod p ,我们就可以求出 b 在 mod p 意义下的逆元,然后乘上 a ,再 mod p ,就是这个乘法逆元的值了。

求法:

First:费马小定理

定理内容:如果 互质,那么 a^(p-1) ≡ 1 (mod p)

  结合逆元方程 a*x ≡ 1 (mod p) ,得到 a*x ≡ a^(p-1)  (mod p)
  根据同余的性质,若p为质数,得到 x ≡ a^(p-2)  (mod p)
  即 x = a^(p-2) mod p, 快速幂 求解即可

Second:欧拉定理

定理内容:如果a,p互质,那么a^φ(p) ≡ 1 (mod p),当 为质数时,φ(p)=p-1

  同理,结合同余方程,得 x=a^(p-2) mod p,快速幂求解即可

  (这只是两种不同的证明,代码是相同的)

技术分享图片
 1 #include<cstdio> 
 2 #define ll long long
 3 using namespace std;
 4 int n,p;
 5 inline ll ksm(ll a,ll b){
 6     ll ans=1;
 7     a%=p;
 8     while(b){
 9         if(b&1) ans=ans*a%p;
10         a=a*a%p;
11         b>>=1;
12     }
13     return ans%p;
14 }
15 void write(ll x){
16     if(x<0) putchar(-),x=-x;
17     if(x>9) write(x/10);putchar(x%10^48);
18 }
19 int main(){
20     scanf("%d%d",&n,&p);
21     for(int i=1;i<=n;i++)
22         write(ksm(i,p-2)),putchar(
);
23     return 0;
24 }
TLE_83分

 

Third:解不定方程 

解同余方程 ax≡1 (mod p) 等价于 解不定方程 ax+py=1
Exgcd求解即可(不会 请左转 Exgcd 
由于 是质数,那么gcd(a,p)=1;
即求解不定方程 ax+by=gcd(a,b);

技术分享图片
 1 #include<cstdio>
 2 using namespace std;
 3 int x,y;
 4 void exgcd(int a,int b){
 5     if(!b){x=1,y=0;return ;}
 6     exgcd(b,a%b);
 7     int t=x;
 8     x=y,y=t-a/b*y;
 9 }
10 void write(int x){
11     if(x>9) write(x/10);
12     putchar(x%10^48);
13 }
14 int main(){
15     int n,p;
16     scanf("%d%d",&n,&p);
17     for(int i=1;i<=n;++i)
18         exgcd(i,p),write((x%p+p)%p),putchar(
);
19     return 0;
20 }
AC_1240ms

 

Forth:线性递推

复杂度 O(n)
递推过程:
令 p=ki+r ; {k=[p/i] (下取整), r=p mod i } (i<p,k<p,r<i)
则有 ki+r≡0(mod p) 
①式左右同乘i^-1*r^-1 得:
k*r^-1+i^-1 ≡ 0 (mod p)
移项 得
i^-1 ≡ -k*r^-1 (mod p)
带入 k=[p/i] (下取整), r=p mod i;
i^-1 ≡ -[p/i]*(p mod i)^-1 (mod p) 
由于 (p mod i) < i ,所以,在求出 i^-1 之前,我们早已求出 (p mod i)^-1
因此用数组 inv[i] 记录i^-1 的逆元)
则 inv[i]=-p/i*inv[p%i]%p;

不要以为到这里就结束了
因为我们需要保证 i^-1>0
所以,我们在②式右边+p p mod p=0,答案不变)
inv[i]=p-p/i*inv[p%i]%p;
当然 inv[1]=1,inv[0]=tan90°(赋值为0);
而且 for循环 要从 开始,防止改变 inv[1] 的值;
至此,证毕。

技术分享图片
 1 #include<cstdio>
 2 #define ll long long
 3 using namespace std;
 4 const int maxn=3e6+5;
 5 ll inv[maxn]={0,1};
 6 int main(){
 7     int n,p;
 8     scanf("%d%d",&n,&p);
 9     printf("1
");
10     for(int i=2;i<=n;i++)
11         inv[i]=(ll)p-(p/i)*inv[p%i]%p,printf("%d
",inv[i]);
12     return 0;
13 }
AC_664ms

 

参考文献:https://www.cnblogs.com/zjp-shadow/p/7773566.html
























以上是关于[模板]乘法逆元的主要内容,如果未能解决你的问题,请参考以下文章

乘法逆元(模板)

P3811 模板乘法逆元

洛谷 P3811 模板乘法逆元 如题

模板乘法逆元

T106021 模板乘法逆元(快速幂)

乘法逆元模板