乘法逆元自学笔记

洛谷上的板子 https://www.luogu.org/problem/P3811
乘法逆元,也就是当ax≡1(mod p) 且gcd(a,p)=1时,x为a在mod p下的乘法逆元 注意,乘法逆元有唯一性(跟同余方程不同,此解必须为最小正整数)
可以说x为a 在mod b 意义下的倒数,
(可以求a/b(modp)的值(前求出b在mod p下的逆元,再乘a模p即可))
题目上是求1~n在mod p下的逆元,且n<p , p为质数
就是 ax%p=1
第一种做法——扩展欧几里得
(exgcd的入门 https://www.cnblogs.com/ffrxy01bt/p/11261296.html
对于每个数暴力用exgcd解决即可 满足exgcd的要求,只需gcd(a,p)=1(当然,不这样的话无解)
时间复杂度:O(nlogn) 由于时间很紧,建议此题不用这个,因为会被卡掉。(虽然它无需要p为质数)
给一个80pts代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map> 
using namespace std;
#define re register int
#define int long long
inline int read(){
    int x=0,ff=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')ff=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*ff;
}
int x,y,xx,yy;
inline void gcd(int a,int b){
    if(b==0){x=1;y=0;return;}
    gcd(b,a%b);
    xx=x;yy=y;
    x=yy;y=(xx-(a/b)*yy);
}
signed main(){
    int n=read(),p=read();
    for(re i=1;i<=n;i++){
        gcd(i,p);
        printf("%d\n",(x%p+p)%p);
    }
    return 0;
}

第二种做法——费马小定理
公式:若p为素数,a为正整数,且a、p互质。 则有a^(p-1) ≡1(mod p)。
这个算法就需要p为质数,且gcd(a,p)=1了
有公式就知道a^(p-1)≡1(mod p) 而求的是 ax%p=1
转换一下 ax%p=a^(p-1)%p x=a^(p-2)%p
所以,一个快速幂也解决了 时间复杂度同样是O(nlogn)
洛谷上只有64pts

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map> 
using namespace std;
#define re register int
#define int long long
inline int read(){
    int x=0,ff=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')ff=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*ff;
}
inline int qsm(int x,int y,int p){
    int s=1;
    while(y){if(y&1)s=s*x%p;x=x*x%p;y>>=1;}
    return s;
}
signed main(){
    int n=read(),p=read();
    for(re i=1;i<=n;i++){
        printf("%d\n",(qsm(i,p-2,p)+p)%p);
    }
    return 0;
}

线性递推
这就是本题的正解了
首先,我们一定能都得到1在mod p下,逆元为1
再加设yi+z=p(i为数i,y为int(p/i),z为p%i) 再假设f[i]表示f[i]为关于i在mod p下的逆元
那么 (y
i+z)≡0(mod p) -z≡yi(mod p) 同乘上f[i]f[z]后 则 -f[i]≡y*f[z] (mod p) 所以 f[i]=-(p/i)f[p%i]%p
然后就是程序了

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map> 
using namespace std;
#define re register int
#define int long long
inline int read(){
    int x=0,ff=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')ff=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*ff;
}
int f[3000005];
signed main(){
    int n=read(),p=read();f[1]=1;printf("1\n");
    for(re i=2;i<=n;i++){
        f[i]=(-(p/i)*f[p%i]%p+p)%p;
        printf("%d\n",f[i]);
    }
    return 0;
}

时间复杂度是O(n)的,但是必须要p为质数,且gcd(a,p)=1

但愿考试遇到的都很水,直接套exgcd就能过

转载于:https://www.cnblogs.com/ffrxy01bt/p/11265935.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值