「exgcd」学习笔记
定义
又名扩展欧几里得算法(辗转相除法)
是用来求
算法证明
拿到一组
目标:求出满足
如果 已知一组
此时结合
那么当 如果 满足时,目标就成了求满足
根据取模运算是
所以方程
那么在根据方程
可见只需求出
将方程
发现原方程的
所以新的方程也可以看做
递归过程中
那么这特就是最后一层,此时就要直接返回了,需要一组
最后一层结束后,就开始返回,知道最上一层,每一层都可以通过下一层的
整个过程就是:以辗转相除的方式向下递进,不断缩小系数,保证会出现有确定解的最后一层
exgcd板子
int exgcd(int a,int b,int &x,int &y) { if(b==0) {x=1;y=0;return a;} int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; }
答案处理
对于更为一般的方程,
事实上,方程
其中
例题1:同余方程
题目链接 同余方程
问题处理
题目问的是满足
问题可以转化成求
问题仍需转化,
简单证明一下:
由最大公因数的定义,
可得这道题中,必须满足
之后通过上述的
答案处理
求出来的
解决方案是:
1.显然这并不会把方程中的任何整数变为非整数
2.加上或减去
已经求出一组
现在给
仍满足其为
因此到最后,如果
x=(x%b+b)%b;
总代码如下
#include<bits/stdc++.h> #define endl '\n' using namespace std; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int a,b,x,y; int exgcd(int a,int b,int &x,int &y) { if(b==0) {x=1;y=0;return a;} int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(a),read(b); exgcd(a,b,x,y); x=(x%b+b)%b; cout<<x; }
例题2:青蛙的约会
题目链接 青蛙的约会
问题处理
两点在数轴上,
设他跳
带进去就是
左右括号内同时减(加)去一个数等式仍然成立:
如果
则
那么原式就可以变为
那么根据该方程,
于是终于将推导式转化成了
就需要思考一下数据是否满足,题目要求如果两点永远无法相遇则输出
用
答案处理
1.借鉴上题的答案处理,我们刚刚求出来的时第一层的结果,但他显然不一定是第一次相遇的结果,例题1答案处理的式子是
2.我们导出来的是
借鉴这两点,推导出这道题的答案处理式子为
x=((x*(c/d))%(b/d)+(b/d))%(b/d);
总代码如下
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int x,y,n,m,l,xx,yy; int exgcd(int a,int b,int &x,int &y) { if(b==0) {x=1;y=0;return a;} int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(xx),read(yy),read(m),read(n),read(l); int a=m-n,b=l,c=yy-xx; if(a<0) a=-a,c=-c; int d=exgcd(a,b,x,y); if((c%d)!=0) cout<<"Impossible"; else cout<<(((x*(c/d))%(b/d)+(b/d))%(b/d)); }
注意事项
放到
int a=m-n,b=l,c=yy-xx; if(a<0) a=-a,c=-c;
例题3:倒酒
题目链接 倒酒
问题处理
问题比较简单,也不需要怎么证明,输入
显然,能够倒出的最小数为
而倒
放到
答案处理
难点在于答案处理,怎么搞出来最小解?
显然,求出的
那我们按照前两题的思路这么打
x=(x%m-m)%m; y=(y%n+n)%n;
但是这样是错误的,
根据我们上面打过的总的答案处理,
同时保证最后输出的是正的最小值,先让
之后,在保证
当
同理,当
如下
x*=-1;a*=-1; while((x<0)||(y<0)) x+=(x<0)?b/d:0, y-=(x>=0)?a/d:0;
需着重理解一下
总代码如下
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int x,y,n,m; int exgcd(int a,int b,int &x,int &y) { if(b==0) {x=1;y=0;return a;} int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); int d=exgcd(n,m,x,y); x*=-1;n*=-1; while((x<0)||(y<0)) x+=(x<0)?m/d:0, y-=(x>=0)?n/d:0; cout<<d<<endl<<x<<' '<<y; }
例题4:一元二次不定方程(模板)
题目链接一元二次不定方程
问题处理
给定一式子
- 求
- 求
- 求解的个数
求
通过
求
不难理解,当
求解的个数
现拿到一组解
使
这样就求出了不定方程的所有解,不难发现,此过程中,共出现了
答案处理
首先设
处理最小值
借鉴例题2和总答案处理中的通解,
x=(x*(c/d)%(b/d)+(b/d))%(b/d); y=(y*(c/d)%(a/d)+(a/d))%(a/d);
但同时,题目要求
x=(x?x:x+(b/d)),y=(y?y:y+(a/d));
处理最大值
根据我们问题处理中最大值的求法,得出最大值为
x_max=(c-y*b)/a,y_max=(c-x*a)/b;
处理解的个数
直接根据我们上面推出的式子
ans=(x_max-x)/(b/d)+1;
或者
ans=(y_max-y)/(a/d)+1;
最后注意
题目要求若不存在
总代码如下
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int T,a,b,c,x,y; int exgcd(int a,int b,int &x,int &y) { if(b==0) {x=1;y=0;return a;} int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(T); while(T--) { read(a),read(b),read(c); x=y=0; int d=exgcd(a,b,x,y); if(c%d) { cout<<-1<<endl; continue; } x=((x*(c/d))%(b/d)+(b/d))%(b/d); y=((y*(c/d))%(a/d)+(a/d))%(a/d); x=(x?x:x+(b/d)),y=(y?y:y+(a/d)); int x_max=(c-y*b)/a,y_max=(c-x*a)/b,ans=(y_max-y)/(a/d)+1; if(x_max<=0||y_max<=0) cout<<x<<' '<<y<<endl; else cout<<ans<<' '<<x<<' '<<y<<' '<<(c-y*b)/a<<' '<<(c-x*a)/b<<endl; } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!