乘法逆元

也挺简单

定义:

\(a,m\) 互质,且满足:

\[a\times x\equiv 1 \pmod{m} \]

则称 \(x\)\(a\)\(\bmod b\) 意义下的乘法逆元,也记为 \(a^{-1}\)

用途:

主要用于 \(\frac{x}{y} \bmod m\) 求值,一般是因为无法化简(例如数太大。

求法:

1. \(exgcd\):

\[ax\equiv 1 \pmod{m} \]

转化得:

\[ax+my=1 \]

运用 \(exgcd\) 求解即可。

CODE(点击查看)
void exgcd(int a,int b,int &x,int &y){
	if(b==0) x=1,y=0;
	else exgcd(b,a%b,y,x),y-=a/b*x;
}
int main(){
	int a,m; scanf("%d%d",&a,&m);
	int x,y;
	exgcd(a,m,x,y);
	printf("%d",(x%m+m)%m); //注意x的取值范围。 
}

2.费马小定理(m是质数):

费马小定理

我们将费马小定理带入原式,得:

\[ax\equiv a^{m-1} \pmod{m} \]

\[x\equiv a^{m-1} \div a \pmod{m} \]

\[x \equiv a^{m-2} \pmod{m} \]

所以我们只需求 \(a^{m-2}\) 即可,用快速幂。

COED(点击查看)
inline int fpow(int a,int b,int mod){
	int ans=1;
	while(b){
		if(b&1) ans=a*ans%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans%mod;
}
int main(){
	int a,m; scanf("%d%d",&a,&m);
	printf("%d",fpow(a,m-2,m));
}

3.线性求法(求得 \(1 \text{ ~ } n\) ):

这种方法在但求一个时慢于前两种,但求 \(1 \text{ 到 } n\) 是优于前两种例题
\(m=k \times a+r (1 \le r < a < m)\)
于是有:

\[ka+r \equiv 0 \pmod{m} \]

乘上 \(r^{-1},a^{-1}\) ,得:

\[kr^{-1}+a^{-1}\equiv 0 \pmod{m} \]

\[a^{-1}\equiv -kr^{-1} \pmod{m} \]

\[a^{-1}\equiv -\left\lfloor\dfrac{m}{a}\right\rfloor\times(m \bmod a)^{-1} \pmod{m} \]

我们又知道,\(1^{-1}\)\(1\) 于是就可以线性推了。

CODE(例题,点击查看)
#include<bits/stdc++.h>
using namespace std;
const int N=3e6+5;
long long n,p,inv[N];

template<typename T>
inline void read(T &x){
    char s=getchar();x=0;bool pd=false;
    while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
    while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
    if(pd) x=-x;
}

int main(){
	read(n),read(p);
	inv[1]=1;
	for(int i=2;i<=n;i++)
		inv[i]=(p-p/i)*inv[p%i]%p;
	for(int i=1;i<=n;i++)
		printf("%lld\n",inv[i]);
}
CODE(主要代码,点击查看)
inv[1]=1;
for(int i=2;i<=n;i++)
	inv[i]=(p-p/i)*inv[p%i]%p;

4.阶乘求逆元:

求:\(1! \text{ 到 } n!\) 的逆元。
给出一个式子,其中 \(v(i)\) 表示 \(i\) 的逆元。

\[v((i-1)!)=\frac{1}{(i-1)!}=\frac{1}{i!}*i=v(i!)*i \]

于是乎又可以线性推了。

然而这种做法还可以求 \(l \text{ 到 } r\) 的逆元。
具体的:

\[v(i)=\frac{1}{i}=\frac{1}{i!}\times(i-1)!=v(i!)\times(i-1)! \]

然后可求。


附:有趣的题:

给定一个长度为 \(n\) 的序列 \(a\) ,求 \(a_1 \text{ 到 } a_n\) 所有数的乘法逆元。
\(1 \le n \le 5e6\) (要求复杂度 \(\operatorname{O} n\)
做法:
先维护前缀积 \(q_i\)
通过:

\[q_i^{-1}\times a_i=\frac{1}{\prod\limits_{j=1}^ia_j}\times a_i=q_{i-1}^{-1} \]

推得 \(q^{-1}\) 的值。
然后通过:

\[q_i^{-1}\times q_{i-1}=\frac{1}{\prod\limits_{j=1}^ia_j}\times\prod\limits_{j=1}^{i-1}a_j=a_i^{-1} \]

得解。

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

posted @ 2023-01-29 16:07  xrlong  阅读(48)  评论(1编辑  收藏  举报