数论 乘法逆元

乘法逆元

定义

ax1(modp) 成立,则称 xa 在模 p 意义下的逆元,同时 a 也是 x 在模 p 意义下的逆元

 ap,x p

 

特殊地, 0 没有乘法逆元, 1 的乘法逆元是 1 本身(与 p 无关)

一般将一个数 a 在模 p 的逆元记为 a1(modp)

意义

  • 在模运算中,有以下公式成立:

    (1)(a+b)%m=((a%m)+(b%m))%m(2)(ab)%m=((a%m)(b%m))%m(3)(a×b)%m=((a%m)×(b%m))%m

  • 在上面的公式中没有除法,即除法是不满足这个性质的

  • 乘法逆元的意义在于,把除以一个数变成乘上它的逆元,在模意义下结果不变

  • 这样就避免了大数字运算

证明:除以一个数变为乘上其逆元,在模意义下结果不变

:ab%p=a×b1%p c=b1(modp) b×c1(modp) b×c%p=1 ab%p=ab×1%p      =ab×b×c%p      =a×c%p      =a×b1%p 

性质

同余的相关性质
  1. 反身性: aa(modp)
  2. 对称性: ab(modp)ba(modp)
  3. 传递性: ab(modp),bc(modp)ac(modp)
  4. 同余式相加减:  kN+,akbk(modp)i=1naii=1nbi(modp)
  5. 同余式相乘:  kN+,akbk(modp)i=1naii=1nbi(modp)
    • 推论1:  kN+,akbk(modp)anbn(modp)
  6. ab(modp)a×kb×k(modp×k)
  7. ab(modp),d | m,dN+ab(modd)
  • 推论: iN+,ab(modmi)ab(modlcm(m1,m2,,mn))
  1. ab(modp)gcd(a,m)=gcd(b,m)
  2. a×cb×c(modp)ab(modpgcd(p,c))
  • 推论: a×cb×c(modp),gcd(c,p)=1ab(modp)

求法

exGcd 扩展欧几里得

 ax1(modp) :(1)     ax+py=1apgcd(a,p)=1 (1):(2)     ax+py=gcd(a,p),(2) x, a1

用法 O(log(a))

template<class t>inline t gcd(t a,t b,t &x,t &y){
    if(b==0){
        x=1,y=0;return;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
inline int sol(int a,int p){
    int x,y;//x is a's Inverse
    if(gcd(a,p,x,y)!=1){//use exGcd to get the solution
        return -1;//if a!⊥p then there's no solution
    }else{
        return (x%p+p)%p;
        //explain: if the 'x' we got is a negative number
        //then (x%p+p)%p can let it become a positive number
        //and in fact, if x≡a^-1 (mod p)
        //then x+p≡a^-1 (mod p)
    }   
}
欧拉函数求逆元

欧拉定理: apaφ(p)1(modp)

上式可变形为 a×aφ(p)11(modp)

由于 ap ,所以 a1aφ(p)1(modp)

用法 O(log(p))

template<class t>t fPow(t x,t y,t p){
    //fast Power to get x^y%p
    t ans=1;
    for(;y;y>>=1){
        if(y&1)ans=ans*x%p;
        x=x*x%p
    }
    return ans;
}
template<class t>t phi(t n){
    t res=n;
    for(t i=2;i*i<=n;i++){
        //公式 phi(n)=n*1/i*(i-1)
        if(n%i==0)res=res/i*(i-1);
        while(n%i==0)n/=i;
    }
    if(n>1)res=res/n*(n-1);
    return res;
}
inline int sol(int a,int p){
	return fPow(a,phi(p)-1,p);
}
筛法求逆元

上述的算法在求单个数的时候表现都不错,但是求多个数的时候效率会较为低下,这时候就需要筛法出场了

 xiai xi=ai1(modp) an{an1} {sn} sn=k=1nk%p(1) si=(si1×ai)%p si1=(si11×ai1)%p(2) si11=(si1×ai)%p(3) ai1=(si1×si1)%p

用法 O(n+log(an)) (欧几里得函数求逆元) 或 O(n+log(p)) (欧拉函数求逆元)

//欧几里得函数求逆元
typedef long long lld;
const int maxn=5e6+12;
lld p;
int n;
lld a[maxn],ina[maxn],s[maxn],ins[maxn];
//a[i] is the number the input gives
//ina[i] is a[i]^-1 mod p
//s[i] is a[1]*a[2]*...*a[i]
//ins[i] is s[i]^-1 mod p
template<class t>inline t gcd(t a,t b,t&x,t&y){
    if(b==0){
        x=1;y=0;return a;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
inline void sol(){
    s[0]=1;//ini
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        s[i]=(s[i-1]*a[i])%p;//according to (1)
    }
    lld x,y;
    gcd(s[n],p,x,y);
    x=(x%p+p)%p;//to make sure x is between (0,p)
    ins[n]=x;
    for(int i=n;i>0;i--){
        ins[i-1]=(ins[i]*a[i])%p;//according to (2)
        ina[i]=(s[i-1]*ins[i])%p;//according to (3)
    }
}
//欧拉函数求逆元同理

例题

有理数取模

给出一个有理数 c=ab ,求 cmod19260817

0a,b1010001

p=19260817

因为 ab=a×b1(modp)

所以我们要求的就是 a×b1modp

因为数据很大,考虑字符处理

{an}ai=xi()

在十进制下: x=(((a1×10+a2)×10+a3)×10+)×10+an1)×10+an

所以 x%p=((a1%p)×10+(a2%p))

做法:

#include<bits/stdc++.h>
using namespace std;
const long long p=19260817;
#define GO(u,v,i) for(register int i=u;i<=v;i++)
template<class t>inline t fr(){
    register t num=0,dis=1;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')dis=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){num=((num<<1)+(num<<3))%p+(ch^48);ch=getchar();}
    return num*dis%p;
}
template<class t>inline void fw(t num){
    if(num>9)fw(num/10);
    putchar(num%10+'0');
}
template<class t>inline void fw(t num,char ch){
    if(num<0)num=-num,putchar('-');
    fw(num);putchar(ch);
}
typedef long long lld;
lld a,b;
template<class t>inline t gcd(t a,t b,t&x,t&y){
    if(b==0){
        x=1,y=0;return a;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
signed main(){
    a=fr<lld>();
    b=fr<lld>();
    lld x,y;
    if(gcd(b,p,x,y)!=1){//no soluion
        printf("Angry!");//as the question requires
    }else
    fw((a*(((x%p)+p)%p))%p,'\n');
    return 0;
}
乘法逆元

给定 n 个正整数 ai

p=109+7,k=998244353

i=1n(ai1×kni)(modp)

证明过程在筛法求逆元

#include<bits/stdc++.h>
using namespace std;
#define GO(u,v,i) for(int i=u;i<=v;i++)
template<class t>inline t fr(){
    register t num=0,dis=1;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')dis=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){num=(num<<1)+(num<<3)+(ch^48);ch=getchar();}
    return num*dis;
}
template<class t>inline void fw(t num){
    if(num>9)fw(num/10);
    putchar(num%10+'0');
}
template<class t>inline void fw(t num,char ch){
    if(num<0)num=-num,putchar('-');
    fw(num);putchar(ch);
}
const int maxn=5e6+12;
typedef long long lld;
const lld p=1e9+7,k=998244353;
int n;
lld a[maxn],s[maxn],sns[maxn],an[maxn];
lld anss;
template<class t>inline t gcd(t a,t b,t&x,t&y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
template<class t>inline t power(t num,t tim){
    t ans=1;
    GO(1,tim,i)ans*=num;
    return ans;
}
inline void sol(){
    n=fr<int>();
    s[0]=1;//初始化
    GO(1,n,i){
        a[i]=fr<lld>();
        s[i]=(s[i-1]*a[i])%p;//根据公式(1)
    }
    lld x,y;
    lld g=gcd(s[n],p,x,y);
    lld ans=x,t=0;
    while((ans=x+p/g*t)<1)t++;
    while((ans=x+p/g*t)>=p)t--;
    sns[n]=ans;// 1/bn 已求出
    lld tmp=1;
    for(int i=n;i>0;i--){
        sns[i-1]=(sns[i]*a[i])%p;//根据公式(2)
        an[i]=(sns[i]*s[i-1])%p;//根据公式(3)
        anss+=(an[i]*tmp)%p;//题目要求
        tmp*=k;tmp%=p;//tmp就是k^(n-i)
    }
    fw(anss%p,'\n');
}
signed main(){
    sol();
    return 0;
}
posted @   Locked_Fog  阅读(114)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示
主题色彩