数论 乘法逆元
乘法逆元
定义
若 成立,则称 是 在模 意义下的逆元,同时 也是 在模 意义下的逆元
特殊地, 没有乘法逆元, 的乘法逆元是 本身(与 无关)
一般将一个数 在模 的逆元记为
意义
-
在模运算中,有以下公式成立:
-
在上面的公式中没有除法,即除法是不满足这个性质的
-
乘法逆元的意义在于,把除以一个数变成乘上它的逆元,在模意义下结果不变
-
这样就避免了大数字运算
证明:除以一个数变为乘上其逆元,在模意义下结果不变
性质
同余的相关性质
- 反身性:
- 对称性:
- 传递性:
- 同余式相加减:
- 同余式相乘:
- 推论1:
- 推论:
- 推论:
求法
exGcd 扩展欧几里得
用法
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)
}
}
欧拉函数求逆元
欧拉定理:
上式可变形为
由于 ,所以
用法
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);
}
筛法求逆元
上述的算法在求单个数的时候表现都不错,但是求多个数的时候效率会较为低下,这时候就需要筛法出场了
用法 (欧几里得函数求逆元) 或 (欧拉函数求逆元)
//欧几里得函数求逆元
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)
}
}
//欧拉函数求逆元同理
例题
有理数取模
给出一个有理数 ,求
令
因为
所以我们要求的就是
因为数据很大,考虑字符处理
设 中
在十进制下:
所以
做法:
#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;
}
乘法逆元
给定 个正整数
求
证明过程在筛法求逆元处
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人