谈到拓展欧几里得算法,就要从欧几里得算法和裴蜀定理说起。
欧几里得算法(辗转相除法)
\(gcd(a,b)=gcd(b,a\mod b)\)
其中,当\(a=0\)时,\(\gcd(a,b)=\gcd(0,b)=b\);\(b=0\)同理
证明:\(gcd(a,b)=gcd(b,a-b)\)
令\(a=b+c\), \(\gcd(a,b)=d\), \(\gcd(b,c)=e\)
主要是证明\(d=e\)
\(a\mod d=0, b\mod d=0,\therefore c\mod d=0,\therefore d\leq e\)
同理,\(e\leq d,\therefore d=e\)
推广一下,相当于\(a\)不断减\(b\)直至\(a<b\),也就是直至\(a\mod b\)
int gcd(int a,int b){
if(b==0)return a;
return gcd(b,a%b);
}
参考:
辗转相除法的原理 _辗转相除法求最大公约数的原理是什么? (shadafang.com)
裴蜀定理(贝祖定理)
设\(a,b\)是不全为0的整数,对任意整数\(x,y\),满足\(\gcd(a,b)|ax+by\),且存在整数\(x,y\),使得\(ax+by=\gcd(a,b)\)
首先证明任意整数\(x,y\),满足\(\gcd(a,b)|ax+by\)
令\(\gcd(a,b)=d,\therefore a=k_{1}d,b=k_{2}d,(k_{1},k_{2})=1\)
\(ax+by=k_{1}dx+k_{2}dy=d(k_{1}x+k_{2}y)\)
\(\therefore \gcd(a,b)|ax+by\)
再证明存在整数\(x,y\),使得\(ax+by=\gcd(a,b)\)
设取整数 \(x_{0},y_{0}\) 时,\(ax+by\) 有最小正整数\(s\),即\(ax_{0}+by_{0}=s\)
\(\because ax_{0}\mod \gcd(a,b)=0\)
且 \(by_{0}\mod \gcd(a,b)=0\)
\(\therefore s\mod \gcd(a,b)=0\)
设\(a=qs+r,(0\leq r<s)\) ---也就是把\(a\)表示成一个整数,其中\(s\)为已知,\(q\)、\(r\)为未知数
\(\therefore r=a-qs=a-q(ax_{0}+by_{0})=a(1-qx_{0})+b(-qy_{0})=ax+by\) ---\(r\)可以表示成 \(ax+by\) 的形式
\(\because s\) 是 \(ax+by\) 的最小正整数
\(\therefore r=0\)
\(\therefore a=qs\) 即 \(a\mod s=0\)
同理,\(b\mod s=0\)
\(\therefore \gcd(a,b)\mod s=0\)
\(\because s\mod \gcd(a,b)=0,\gcd(a,b)\mod s=0,\therefore \gcd(a,b)=s\)
拓展欧几里得算法
拓展欧几里得算法实际上就是求 \(ax+by=\gcd(a,b)\) 的一组整数解
当\(b=0\)时,\(ax+by=ax=\gcd(a,0)=a\)
此时,\(x=1,y=0\) 就是 \(ax+by=\gcd(a,b)\) 的一组整数解
当\(b\neq 0\)时,
由欧几里得算法可知,\(gcd(a,b)=gcd(b,a\mod b)\)
由裴蜀定理可知,
\(\therefore x=y_{1},y=x_{1}-\frac{a}{b}y_{1}\)
利用递归,先求出下一层的\(x_{1},y_{1}\),以此类推,可以求得特解\((x_{0},y_{0})\)
构造通解
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;y=0;
return a;
}
int x1,y1,d;
d=exgcd(b,a%b,x1,y1);
x=y1;y=x1-a/b*y1;
return d;
}
求\(ax+by=c\)的一组整数解
由拓展欧几里得算法可知,我们可以求\(ax+by=\gcd(a,b)\)的一组整数解
那么,如果 \(c\mod gcd(a,b)=0\) ,我们只需在等式左右两边同乘\(c/\gcd(a,b)\) ,就能求得 \(ax+by=c\)的一组整数解
若\(c\mod gcd(a,b)\neq0\),则无整数解。这个可以有裴蜀定理可知,证明对于不定方程 \(ax+by=m\) 其有整数解的充要条件是 \(\gcd(a,b)|m\) 即可。
例题
题目
题解
根据指针的转动,可以列出 (只是大致的,未考虑加一/减一)$$t+ax=re+my$$其中,\(x\)表示顺时针或逆时针转动的次数,\(re\) 表示指针最后停的位置,\(y\) 表示转动的圈数
移动,$$ax-my=re-t$$
我们就可以根据拓展欧几里得算法求得 \(\gcd(a,m)\),以及对应的 \((x_{0},y_{0})\)
\(re-t\) 是当\(re\)取最小值时,\(\gcd(a,m)|re-t\)
\(\because re\)的范围是\([1,m]\),所以 \(re=(t-1)\%d+1\)
这样就可以求得特解\(x=x*(re-t)/d)\)(后面还要加mod)
最后再根据通解,求出 \(x\) 的最小值。
代码
#include <bits/stdc++.h>
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;y=0;
return a;
}
int x1,y1,d;
d=exgcd(b,a%b,x1,y1);
x=y1;y=x1-a/b*y1;
return d;
}
void solve(){
int t,a,m;
cin>>t>>a>>m;
int x,y;
int d=exgcd(a,m,x,y);
t=(t-1)%d+1-t; //这里就省略了re
x=((x*t/d)%m+m)%m; //特解
x=(x%(m/d)+(m/d))%(m/d); //通解
cout<<min(x,m/d-x)<<endl;
}
signed main()
{
ios;
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
标签:gcd,int,一日之计,欧几里得,re,算法,therefore,ax,mod
From: https://www.cnblogs.com/cdag/p/18102603