[模板]乘法逆元
Posted risinggods
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[模板]乘法逆元相关的知识,希望对你有一定的参考价值。
本博客所有代码基于题目 luogu_P3811
逆元:
一般用于求 (a/b) mod p
定义:
若 a*x ≡ 1 (mod p) ,且 a 与 p 互质,那么我们就能定义: x 为 a 的逆元,记为 a^-1 ,所以我们也可以称 x 为 a 的倒数(mod p意义下)。
所以对于 (a/b) mod p ,我们就可以求出 b 在 mod p 意义下的逆元,然后乘上 a ,再 mod p ,就是这个乘法逆元的值了。
求法:
First:费马小定理
定理内容:如果 a , p 互质,那么 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)=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 }
Third:解不定方程
解同余方程 ax≡1 (mod p) 等价于 解不定方程 ax+py=1
Exgcd求解即可(不会 请左转 Exgcd )
由于 p 是质数,那么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 }
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 ( i 的逆元)
则 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循环 要从 2 开始,防止改变 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 }
参考文献:https://www.cnblogs.com/zjp-shadow/p/7773566.html
以上是关于[模板]乘法逆元的主要内容,如果未能解决你的问题,请参考以下文章