逆元

逆元用于求 $ \frac{a}{b} (mod\ p)$ 的结果

第一种方法:拓展欧几里得

  • 利用拓欧求解同余方程:\(a*x ≡ c\ (mod\ b)\)\(c = 1\) 的情况
  • 就可以转化成求解 \(a*x+b*y=1\) 这个方程
void exgcd(ll a,ll b,ll &x,ll &y)  //解 ax + by = gcd(a,b) 的一组整数解 
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b,x,y);
	ll s_x=x;
	ll s_y=y;
	x=s_y;
	y=s_x-(a/b)*s_y;  //时间复杂度近似为logn 
}
// 另一种写法,顺便把最大公约数算出来
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if(b == 0) {
        x = 1, y = 0;
        return a;
    }
    ll g = exgcd(b, a % b, x, y);
    ll temp = x; x = y, y = temp - a / b * y;
    return g;
}

//拓展欧几里得求逆元
ll inv1(ll a)
{
	ll x,y;
	exgcd(a,mod,x,y);
	return (x%mod+mod)%mod;
 }

第二种方法:快速幂

  • 这个做法要利用费马小定理

\(p\)为素数, \(a\)为正整数,且\(a,p\)互质,则有\(a^{p - 1}≡1(mod\ p)\)

  • 带入即可得到:$$x≡a^{p - 2}(mod\ p)$$
ll fastpow(ll a,ll b)  //快速幂(最多算到2^62) 
{
	ll res=1;
	while(b){
		if(b&1) res=(res*a)%mod;  //取模运算 
		a*=a;
		b>>=1;
	}
	return res;
}

//2.费马小定理求逆元
ll inv2(ll a)
{
	return fastpow(a,mod-2);
 } 

第三种方法:线性算法

  • 用于求一连串数字对于一个 \(mod\ p\) 的逆元。
inv[1] = 1;
for(int i = 2; i <= n; i ++)
    inv[i] = (p - p / i) * inv[p % i] % p;

一个用于第三种方法的例题:
P3811 【模板】模意义下的乘法逆元

点击查看代码
#include<cstdio>
#define ll long long
using namespace std;
const int maxn = 3e6 + 5;
ll inv[maxn];
int main(){
    inv[0] = 0, inv[1] = 1;
    int n, p;
    scanf("%d %d", &n, &p);
    printf("1\n");
    for(int i = 2; i <= n; i ++)
        inv[i] = ((ll)p - (p / i)) * inv[p % i] % p, printf("%lld\n", inv[i]);
    return 0;
}
posted @ 2024-08-10 09:44  懒羊羊爱吃灰太狼  阅读(17)  评论(0)    收藏  举报