数论 乘法逆元
乘法逆元
定义
若 \(ax\equiv 1\pmod p\) 成立,则称 \(x\) 是 \(a\) 在模 \(p\) 意义下的逆元,同时 \(a\) 也是 \(x\) 在模 \(p\) 意义下的逆元
\(\because\ 上述等式成立,当且仅当a\perp p,且x\perp\ p\)
\(\therefore\ 若不互质,则逆元不存在\)
特殊地, \(0\) 没有乘法逆元, \(1\) 的乘法逆元是 \(1\) 本身(与 \(p\) 无关)
一般将一个数 \(a\) 在模 \(p\) 的逆元记为 \(a^{-1}\pmod p\)
意义
-
在模运算中,有以下公式成立:
\[\begin{align} (a+b)\%m&=((a\%m)+(b\%m))\%m\tag{1}\\ (a-b)\%m&=((a\%m)-(b\%m))\%m\tag{2}\\ (a\times b)\%m&=((a\%m)\times(b\%m))\%m\tag{3} \end{align} \] -
在上面的公式中没有除法,即除法是不满足这个性质的
-
乘法逆元的意义在于,把除以一个数变成乘上它的逆元,在模意义下结果不变
-
这样就避免了大数字运算
证明:除以一个数变为乘上其逆元,在模意义下结果不变
性质
同余的相关性质
- 反身性: \(a\equiv a\pmod p\)
- 对称性: \(a\equiv b\pmod p\Rightarrow b\equiv a\pmod p\)
- 传递性: \(a\equiv b\pmod p,b\equiv c\pmod p\Rightarrow a\equiv c\pmod p\)
- 同余式相加减: \(\forall\ k\in\N^+,a_k\equiv b_k\pmod p\Rightarrow\sum\limits_{i=1}^na_i\equiv\sum\limits_{i=1}^nb_i\pmod p\)
- 同余式相乘: \(\forall\ k\in\N^+,a_k\equiv b_k\pmod p\Rightarrow\prod\limits_{i=1}^na_i\equiv\prod\limits_{i=1}^nb_i\pmod p\)
- 推论1: \(\forall\ k\in\N^+,a_k\equiv b_k\pmod p\Rightarrow a^n\equiv b^n\pmod p\)
- \(a\equiv b\pmod p\Rightarrow a\times k\equiv b\times k\pmod {p\times k}\)
- \(a\equiv b\pmod p,d\ |\ m,d\in\N^+\Rightarrow a\equiv b\pmod d\)
- 推论: \(\forall i\in\N^+,a\equiv b\pmod{m_i}\Rightarrow a\equiv b\pmod{\text{lcm}(m_1,m_2,\dots,m_n)}\)
- \(a\equiv b\pmod p\Rightarrow \gcd(a,m)=\gcd(b,m)\)
- \(a\times c\equiv b\times c\pmod p\Rightarrow a\equiv b\pmod{\frac p{\gcd(p,c)}}\)
- 推论: \(a\times c\equiv b\times c\pmod p,\gcd(c,p)=1\Rightarrow a\equiv b\pmod p\)
求法
exGcd 扩展欧几里得
用法 \(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)
}
}
欧拉函数求逆元
欧拉定理: \(a\perp p\Rightarrow a^{\varphi(p)}\equiv1\pmod p\)
上式可变形为 \(a\times a^{\varphi(p)-1}\equiv1\pmod p\)
由于 \(a\perp p\) ,所以 \(a^{-1}\equiv a^{\varphi(p)-1}\pmod p\)
用法 \(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);
}
筛法求逆元
上述的算法在求单个数的时候表现都不错,但是求多个数的时候效率会较为低下,这时候就需要筛法出场了
用法 \(O(n+\log(a_n))\) (欧几里得函数求逆元) 或 \(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=\frac ab\) ,求 \(c\mod 19260817\)
\(0\le a,b\le 10^{10001}\)
令 \(p=19260817\)
因为 \(\frac ab=a\times b^{-1}\pmod p\)
所以我们要求的就是 \(a\times b^{-1}\mod p\)
因为数据很大,考虑字符处理
设 \(\{a_n\}\) 中 \(a_i=x的第i位数(从左至右)\)
在十进制下: \(x=(\dots((a_1\times10+a_2)\times10+a_3)\times 10+\dots)\times 10+a_{n-1})\times 10+a_n\)
所以 \(x\%p=(\dots(a_1\%p)\times10+(a_2\%p))\dots\)
做法:
#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\) 个正整数 \(a_i\)
\(p=10^9+7,k=998244353\)
求 \(\sum\limits_{i=1}^n(a_i^{-1}\times k^{n-i})\pmod p\)
证明过程在筛法求逆元处
#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;
}

浙公网安备 33010602011771号