逆元与(扩展)欧拉定理
本文主要讲了逆元、(扩展)欧拉定理。
快速模幂
假设为奇数,则,否则
这样可以写一个循环来实现,在时结束循环即可。
#include <bits/stdc++.h>
using namespace std;
int a,b,c,x,y,p,m=1;
int main() {
for(scanf("%d%d%d",&a,&b,&p),x=a,y=b; b; b>>=1) {
if(b&1)m=(m*1ll*a)%p;//b为奇数
a=(a*1ll*a)%p;//将a平方
}
printf("%d^%d mod %d=%d\n",x,y,p,m);
return 0;
}
乘法逆元
对于关于的同余方程,当且仅当时有解,有解时最小正整数解即为在模意义下的逆元,记号为。
例如在模意义下的逆元即为。
线性筛逆元
首先在模意义下的逆元必定为
我们考虑模意义下的逆元。
设,则(带余除法的性质)
则
把移项到右边,两边同时乘上
把代入
代码:
#include <bits/stdc++.h>
using namespace std;
int mod,n,inv[20000528];
int main() {
scanf("%d%d",&n,&mod),inv[1]=1,printf("1\n");
for(int i=2; i<=n; ++i) {
inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
printf("%d\n",inv[i]);
}
return 0;
}
欧拉定理
若,则
证明:(可不看)
剩余类
剩余类,亦称同余类。
我们把(所有)对模同余的整数构成的一个集合叫做模的一个剩余类。
即(为任意整数)所表示的所有数就是与模同余的剩余类
例如就是与模同余的剩余类
简化剩余系
简化剩余系也称既约剩余系或缩系
在与 与互素的整数 模同余的所有剩余类中,从每一个类中各任取一个数作为代表组成的集合,叫做模的一个简化剩余系。
例如模的一个简化剩余系是,模的一个简化剩余系是
根据的定义,模的简化剩余系有个元素。
根据简化同余系的定义,每个模的简化剩余系的所有元素的乘积模相等。
证明
设是一个以为模的缩系。
而且,则
所以也是一个以为模的缩系。
所以。
所以
费马小定理
当为质数时,
证明:
当不互质时即,显然成立
当互质时,根据欧拉定理得
而且为质数时,,代入上式后两边同时乘即可得证。
变式
当时,
即模意义下的逆元为(当为质数时,的逆元为)
快速幂求逆元
直接用欧拉定理的式子即可
#include<bits/stdc++.h>
using namespace std;
int getphi(int n) {
int tmp=n,ans=n;
for(int i=2; i*i<=tmp; ++i)
if(!(tmp%i)) {
ans-=ans/i;
while(!(tmp%i))tmp/=i;
}
return tmp>1?ans-ans/tmp:ans;
}
int a,b,p,m=1;
int main() {
for(scanf("%d%d",&a,&p),b=getphi(p)-1; b; b>>=1) {
if(b&1)m=(m*1ll*a)%p;
a=(a*1ll*a)%p;
}
printf("%d\n",m);
return 0;
}
扩展欧几里得求逆元
扩展欧几里得相关见同余基础数论详解
这题其实就是求模意义下的逆元。
有解说明,那么,直接用扩展欧几里得解同余方程即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,mod,x,y,d;
void exgcd(ll a,ll b,ll&x,ll&y,ll&gcd) {
if (!b) gcd=a,x=1,y=0;
else exgcd(b,a%b,y,x,gcd),y-=a/b*x;
}
int main() {
scanf("%lld%lld",&a,&mod);
exgcd(a,mod,x,y,d);
printf("%lld\n",(x%mod+mod)%mod);//记得取模
return 0;
}
求多个数的逆元
我们这题要求模意义下的逆元,我们可以试着用求
设,可以预处理出
再设,可得
接下来就是要考虑如何处理:
预处理后,,是
之后从到的循环,算出,同时算出即可。
总复杂度
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x=0,c=getchar();
for(; c<=47||c>=58; c=getchar());
for(; c>=48&&c<=57; c=getchar())x=(x<<3)+(x<<1)+(c&15);
return x;
}
const int MAXN=1<<23;
int n,mod,k,b[MAXN],s[MAXN],a[MAXN],inv[MAXN],ans;
int fastpow(int a,int k) {
int base=1;
for(; k; a=((0ll+a)*a)%mod,k>>=1)
if(k&1)
base=((0ll+base)*a)%mod;
return base;
}
int main() {
s[0]=1;
n=read(),mod=read(),k=read();
for(int i=1; i<=n; ++i) {
a[i]=read();
s[i]=s[i-1]*1ll*a[i]%mod;//预处理si
}
b[n+1]=fastpow(s[n],mod-2);
for(int i=n; i>=1; --i) {
inv[i]=b[i+1]*1ll*s[i-1]%mod;//逆元
b[i]=b[i+1]*1ll*a[i]%mod;//bi
}
int tmp=k;
for(int i=1; i<=n; i++) {
ans=(ans+inv[i]*1ll*tmp)%mod;//计算答案
tmp=(tmp*1ll*k)%mod;
}
printf("%d\n",ans);
return 0;
}
有理数取余
直接用上面说的求逆元即可。
#include<bits/stdc++.h>
using namespace std;
const int mod=19260817;
inline int read() {
int x=0,c=getchar();
for(; c<=47||c>=58; c=getchar());
for(; c>=48&&c<=57; c=getchar())x=((x<<3)+(x<<1)+(c&15))%mod;
return x;
}
int fastpow(int a,int k) {
int base=1;
while(k) {
if(k&1) base=base*1ll*a%mod;
a=a*1ll*a%mod;
k>>=1;
}
return base;
}
int a,b;
int main() {
a=read(),b=read();
printf("%d\n",a*1ll*fastpow(b,mod-2)%mod);
return 0;
}
扩展欧拉定理
当时,
证明:
首先时,,结论成立。
任取一个质数,就可以写成,其中。
则(是积性函数)
由欧拉定理得,则
可以设,将式子两边乘上:
,即
当时,
且,所以在时,上式成立。
改写一下上面的结论:
当即时,
也就是
把分解质因数后得到的所有质数都会满足上式,把它们乘起来就是也满足。
模板题代码:
#include <bits/stdc++.h>
using namespace std;
int m,a,b,phi;
int getphi(int n) {//求phi
int tmp=n,ans=n;
for(int i=2; i*i<=tmp; ++i)
if(!(tmp%i)) {
ans-=ans/i;
while(!(tmp%i))tmp/=i;
}
return tmp>1?ans-ans/tmp:ans;
}
void read(int&b) {
bool gt=0;
char c=getchar();
for(; c<'0'||c>'9'; c=getchar());
for(; c>='0'&&c<='9'; c=getchar()) {
b=(b<<3)+(b<<1)+(c^48);
gt|=b>=phi;//判断b是否大于phi
b%=phi;
}
if(gt)b+=phi;//不大于就不能加
}
int quickpow(int a,int b,int p) {//快速幂
int m=1;
while(b) {
if(b&1)m=(m*1ll*a)%p;
a=(a*1ll*a)%p;
b>>=1;
}
return m;
}
int main() {
scanf("%d%d",&a,&m),phi=getphi(m),read(b);
printf("%d\n",quickpow(a,b,m));
return 0;
}
一道题
这个明显大于,根据扩展欧拉定理
这样就可以递归,直到时返回即可。
#include<bits/stdc++.h>
using namespace std;
int fastpow(int a,int k,const int mod) {
int base=1;
while(k) {
if(k&1)base=base*1ll*a%mod;
a=a*1ll*a%mod;
k>>=1;
}
return base;
}
int phi(int n) {
int tmp=n;
for(int i=2; i*i<=n; ++i)
if(!(n%i))
for(tmp-=tmp/i; !(n%i); n/=i);
return n>1?tmp-tmp/n:tmp;
}
int solve(int mod) {
int t=phi(mod);
return mod==1||mod==2?0:fastpow(2,solve(t)+t,mod);//递归
}
int n,t;
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
printf("%d\n",solve(n));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】