同余基础数论详解
本文主要讲了扩展欧几里得、(扩展)中国剩余定理。
扩展欧几里得
小结论1:当为整数时,最小正整数值为。
我们设最小值为,对应不定方程的解为。
可以发现
那么
再设
则
可得,是的形式
而且是最小正整数值,为整数,
可得即
同理可得,那么
而且上面也推出最后得到
小结论2:当为整数时,有整数解的充分必要条件是
充分性:由小结论1得,有整数解,只需将解乘上相应的倍数即是的解。
必要性:得
扩展欧几里得
我们可以发现,,那我们可以试试找出与的解的关系。
首先有
把代入后整理一下得到
可以得到其中一组解为
代码递归到时,方程就变为,一组解就是,还可以顺便求
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;
}
有特殊要求的解不定方程
这题比普通的不定方程多了一些要求,其中输出-1
当且仅当不是的因数,所以我们这里只讨论有解的情况。
由于方程和解相同,下面就只考虑的情况。
我们可以看一下不定方程解的性质:
比如我们已经用exgcd
求出一组解
可以发现当为任意整数时,(把括号拆开就是)
所以这个方程的通解为
假设使也为正整数的的最小正整数值为,最大正整数值为
根据通解的形式,都对应一组正整数解。
也就是正整数解得个数为。
我们可以先求出的最小正整数值:
由通解的形式得:,则最小正整数值就是,这样我们同事还可以求出最大值
同理最小值就是或,
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
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;
}
ll a,b,c,d,x,y,x2,y2,t,z;
int main() {
scanf("%d",&t);
while(~scanf("%lld%lld%lld",&a,&b,&c)) {
exgcd(a,b,x,y,d);
if(c%d) {//无解
printf("-1\n");
continue;
}
a/=d,b/=d,c/=d,x*=c,y*=c;//先求出x0,y0,除以gcd后才是我们讨论的情况
x=x%b>0?x%b:x%b+b,y=(c-a*x)/b;//算出x1,y1
y2=y%a>0?y%a:y%a+a,x2=(c-b*y2)/a;//算出x2,y2
if(y>0) {
printf("%lld %lld %lld %lld %lld\n",(x2-x)/b+1,x,y2,x2,y);
} else {
printf("%lld %lld\n",x,y2);
}
}
return 0;
}
同余方程
将转换成就可以解了。
由小结论2得,若,则有整数解,否则无整数解。
有解的时候,先解的解,再将解乘上即可
有解说明,那么,直接解同余方程即可
注:这其实就是求模意义下的逆元,逆元相关见逆元与(扩展)欧拉定理。
#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;
}
例题:P1516 青蛙的约会
题目即求的。
整理下式子就是
直接解同余方程就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll m,n,x,y,b,c,a,gcd;
ll exgcd(ll a,ll b,ll&x,ll&y,ll&gcd) {
if(!b)x=1,y=0,gcd=a;
else exgcd(b,a%b,y,x,gcd),y-=a/b*x;
}
int main() {
scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&b),a=m-n,c=y-x;
if(a<0)a=-a,c=-c;
exgcd(abs(a),b,x,y,gcd);
if(c%gcd)puts("Impossible");
else printf("%lld\n",(c/gcd*x%(b/gcd)+b/gcd)%(b/gcd));
return 0;
}
多组同余方程的通解
例题,三倍经验:(T3需要改下读入)
现在比如要求的最小正整数解
我们假设前个方程的最小正整数解为,
那么我们可以得到前个方程的所有解都可以表示成(为整数)
现在我们就是要找一个使也满足第个方程
那么即
转换不定方程
如果不为整数,则整个方程都无解,否则用扩展欧几里得算出后算出即可。
也就是说,就能将第个方程也加进去。
我们只要让初始值为,初始值为,之后把后面个方程逐个加入即可。
注:一定要对取模,因为会乘上,还要对取模,对答案虽不会有影响,但下面的代码的***
处可能导致精度溢出
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
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;
}
ll mul(ll ai,ll bi,ll p) {//快速计算a*b%p(保证精度)
ll ansp=0,x=(ai+p)%p;
bi=(bi+p)%p;
while(bi!=0) {
if(bi&1)ansp=(ansp+x)%p;
x=(x+x)%p;
bi>>=1;
}
return (ansp%p+p)%p;
}
ll mod,ans,x,y,b,c,d,a,m;
int main() {
scanf("%d",&n);
scanf("%lld%lld",&mod,&ans);//第一个方程的解即是a1
for(int i=1; i<n; ++i) {
scanf("%lld%lld",&m,&a);
exgcd(mod,m,x,y,d);
b=ans-a,c=m/d;//b取了相反数,后面就用了减
if(b%d) {//判断无解
ans=-1;
break;
}
x=(mul(b/d,x,c)+c)%c;
ans-=x*mod;// ***
mod*=c;//计算最小公倍数
ans=(ans%mod+mod)%mod;
}
printf("%lld\n",ans);
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】