[数论]拓展欧几里得算法
Posted yanick
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数论]拓展欧几里得算法相关的知识,希望对你有一定的参考价值。
欧几里得算法(辗转相除法)
用来求解最大公约数
1 int gcd(int a,int b){ 2 return b ? gcd(b,a%b) : a; 3 }
在 #include<algorithm> 中也可以直接调用 __gcd(a,b)
拓展欧几里得算法
求解不定方程:
引理:存在 x , y 使得 ax+by=gcd(a,b)
设a,b,c为任意整数,若方程ax+by=c的一组解是(x0,y0),则它的任意整数解都可以写成(x0+k*b/gcd(a,b),y0-k*a/gcd(a,b)),k取任意整数
1 typedef long long ll; 2 3 ll exgcd(ll a,ll b) 4 { 5 if(b){ 6 ll r=exgcd(b,a%b); 7 ll k=x; 8 x=y; 9 y=k-a/b*y; 10 return r; 11 } 12 else{ 13 x=1,y=0; 14 return a; 15 } 16 }
解线性同余方程
关于 x 的模方程 ax%b=c 的解,方程转换为 ax+by=c 其中 y 一般为非正整数,则问题变为用 exgcd 解不定方程
计算乘法逆元
若 a*x≡1(mod b) ,且 a与 b互质,那么我们就能定义: x为 a的逆元
这就是利用拓欧求解线性同余方程 a*x≡c(mod b) 的 c=1 的情况。我们就可以转化为解 a*x + b*y = 1这个方程
1 typedef long long ll; 2 3 void exgcd(ll a,ll b)//扩展欧几里得算法求乘法逆元,x为a的逆元 4 { 5 if(!b){ 6 x=1,y=0; 7 return; 8 } 9 exgcd(b,a%b); 10 ll k; 11 k=x; 12 x=y; 13 y=k-(a/b)*y; 14 }
一些例题
zoj 3609
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 ll gcd; 5 6 ll exgcd(ll a,ll b,ll &x,ll &y) 7 { 8 if(b==0){ 9 x=1,y=0; 10 return a; 11 } 12 ll ans=exgcd(b,a%b,x,y); 13 ll temp=x; 14 x=y; 15 y=temp-a/b*y; 16 return ans; 17 } 18 19 ll cal(ll a,ll b,ll c) 20 { 21 ll x,y; 22 gcd=exgcd(a,b,x,y); 23 ll ans=(x%b+b)%b; 24 if(!ans) 25 ans=b; 26 return ans; 27 } 28 29 int main() 30 { 31 ll a,b,t; 32 cin>>t; 33 while(t--){ 34 cin>>a>>b; 35 ll ans=cal(a,b,1); 36 if(gcd!=1) 37 cout<<"Not Exist\n"; 38 else 39 cout<<ans<<endl; 40 } 41 return 0; 42 }
zoj 3593
1 /*根据公式ax+by=c;当x,y同号时等于max(x,y) 2 当a,b异号时等于(abs(x)+abs(y)) 3 因为a,b大于0,所以不管x,y同号还是异号都是当x,y,最接近时,答案最小 4 写出通式 x=x1+b/(gcd)*k, y=y1-a/gcd*k 5 假设x,y相等, k=(y-x)/(a+b),因为x,y不一定相等,所以枚举k附近的点*/ 6 #include<bits/stdc++.h> 7 using namespace std; 8 typedef long long ll; 9 ll x,y; 10 11 ll exgcd(int a,int b){ 12 if(b){ 13 ll n=exgcd(b,a%b); 14 ll xt=x; 15 x=y,y=xt-a/b*y; 16 return n; 17 } 18 x=1,y=0; 19 return a; 20 } 21 22 int main(){ 23 ll n,m,t,a,b,c; 24 cin>>t; 25 while(t--){ 26 cin>>n>>m>>a>>b; 27 c=n-m; 28 ll ans=exgcd(a,b); 29 if(c%ans!=0){ 30 cout<<-1<<endl; 31 continue; 32 } 33 c/=ans,a/=ans,b/=ans; 34 x*=c,y*=c; 35 ll k=(y-x)/(a+b); 36 ans=10000000000ll; 37 for(ll i=k-1;i<k+2;i++){ 38 ll xx; 39 ll x0=x+b*i,y0=y-a*i; 40 if(x0<=0&&y0>=0||y0<=0&&x0>=0) 41 xx=abs(x0)+abs(y0); 42 else 43 xx=max(abs(x0),abs(y0)); 44 if(xx<ans) 45 ans=xx; 46 } 47 cout<<ans<<endl; 48 } 49 return 0; 50 }
poj 1061
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 typedef long long ll; 5 ll x,y; 6 7 ll exgcd(ll a,ll b) 8 { 9 if(b){ 10 ll r=exgcd(b,a%b); 11 ll k=x; 12 x=y; 13 y=k-a/b*y; 14 return r; 15 } 16 else{ 17 x=1,y=0; 18 return a; 19 } 20 } 21 22 int main() 23 { 24 ll p,q,m,n,l; 25 scanf("%lld%lld%lld%lld%lld",&p,&q,&m,&n,&l); 26 ll a=n-m,b=l,c=p-q; 27 ll ans=exgcd(a,b); 28 if(c%ans) 29 printf("Impossible\n"); 30 else{ 31 ll k=c/ans,t=b/ans; 32 x*=k; 33 if(t<0) 34 t=-t; 35 x=(x%t+t)%t; 36 cout<<x<<endl; 37 } 38 return 0; 39 }
hdu 1576
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll x,y; ll exgcd(int a,int b){ if(b){ ll n=exgcd(b,a%b); ll xt=x; x=y,y=xt-a/b*y; return n; } x=1,y=0; return a; } int main(){ ll n,m,t,a,b=9973,c; cin>>t; while(t--){ cin>>n>>a; ll ans=exgcd(a,b); x=(x%b+b)%b; ans=n*(x%b)%b; cout<<ans<<endl; } return 0; }
以上是关于[数论]拓展欧几里得算法的主要内容,如果未能解决你的问题,请参考以下文章