学习笔记:逆元

如果 a ∗ x ≡ 1   ( m o d   p ) a*x≡1\ (mod\ p) ax1 (mod p),那么, x x x就是 a a a的逆元,相互的, a a a也是 x x x的逆元。那么逆元有什么用呢?

首先我们需要知道,取余运算对除法是不适用的, a b   %   p ≠ a   %   p b   %   p   %   p \frac{a}{b}\ \%\ p ≠ \frac{a\ \%\ p}{ b\ \%\ p}\ \%\ p ba % p=b % pa % p % p
如果我们要计算若干个数相乘起来除以某个数时,并不能像乘法那样边乘边取余,所以我们可以将 a b   %   p \frac{a}{b}\ \%\ p ba % p转化为 a ∗ b − 1   %   p a*b^{-1}\ \%\ p ab1 % p ( b − 1 b^{-1} b1是指 b b b在模 p p p下的逆元,不是 b b b − 1 -1 1次方),这样就可以边乘边取余。


逆元的求法:
方法一:欧拉函数

在这里插入图片描述

int power(int a,int b,int p)
{
	int res=1%p;
	while(b)	{ if(b&1) res=res*a%p; a=a*a%p; b>>=1; }
	return res;
}

int inverse(int a,int p) 
{
	
	int phi=a,x=a;
	for(int i=2;i<=x/i;i++)
		if(x%i==0)
		{
			phi=phi/i*(i-1);
			while(x%i==0)	x/=i;
		}
	if(x>1)	phi=phi/x*(x-1);
	
	return power(a,phi-1,p);
}

方法二:扩展欧几里得

在这里插入图片描述

int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)  { x=1; y=0; return a; }
	
	int g=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return g;
}

int inverse(int a,int p)
{
	int x,y;
	int g=exgcd(a,p,x,y);
	return g==1?(x%p+p)%p : -1; //x有可能是负数,转化为正数 
}

update
2021/9/29

又发现了一种神奇的算法,线性递推求逆元,不过适应范围很狭窄,在特定题目中使用,如LuoguP3811

该算法用于求一连串数字对于一个 m o d   p mod\ p mod p的逆元。
给你一个 n n n p p p,要你算出 1 1 1 ~ n n n的所有数对 p p p的逆元,前面两种做法会超时。

具体求法是:

对于 1 1 1 ~ n n n中的任意一个数 i i i,设 p = k ∗ i + r p=k*i+r p=ki+r ( k = p i , r = p % i ) (k=\frac{p}{i},r=p\%i) (k=ip,r=p%i)

显然有 k ∗ i + r ≡ 0   ( m o d   p ) k*i+r\equiv0\ (mod\ p) ki+r0 (mod p)

i − 1 i^{-1} i1 r − 1 r^{-1} r1分别是 i i i r r r的模 p p p下的逆元。

那么
i − 1 r − 1 ( k ∗ i + r ) ≡ 0   ( m o d   p ) i^{-1}r^{-1}(k*i+r)\equiv0\ (mod\ p) i1r1(ki+r)0 (mod p)

k r − 1 + i − 1 ≡ 0   ( m o d   p ) kr^{-1}+i^{-1}\equiv0\ (mod\ p) kr1+i10 (mod p)

i − 1 ≡ − k r − 1   ( m o d   p ) i^{-1}\equiv-kr^{-1}\ (mod\ p) i1kr1 (mod p)

i − 1 ≡ − p i ∗ ( p % i ) − 1   ( m o d   p ) i^{-1}\equiv-\frac{p}{i}*(p\%i)^{-1}\ (mod\ p) i1ip(p%i)1 (mod p)

为了保证 i − 1 > 0 i^{-1}>0 i1>0,这里我们对等式右边加上一个 p ∗ ( p % i ) − 1 p*(p\%i)^{-1} p(p%i)1,因为是对 p p p取模,这里加上一个 p p p的倍数,对结果是没有影响的。得到:

i − 1 ≡ ( p − p i ) ∗ ( p % i ) − 1   ( m o d   p ) i^{-1}\equiv(p-\frac{p}{i})*(p\%i)^{-1}\ (mod\ p) i1(pip)(p%i)1 (mod p)

于是乎,就得到了逆元的递推公式, i i i的逆元就可以由 p % i p\%i p%i的逆元推算出来。

i n v [ i ] = ( p − p i ) ∗ i n v [ p % i ] % p inv[i]=(p-\frac{p}{i})*inv[p\%i]\%p inv[i]=(pip)inv[p%i]%p

P 3811 代 码 P3811代码 P3811

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

typedef long long LL;

const int N = 3e6 + 10;

LL n, p, inv[N];

int main()
{
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif

    scanf("%lld%lld", &n, &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]);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值