歐幾里得+擴充套件歐幾里得
歐幾里得演算法
歐幾里德演算法又稱輾轉相除法,用於計算兩個正整數a,b的最大公約數(gcd)。
其計算原理依賴於下面的定理:
定理:gcd(a,b) = gcd(b,a mod b) (a>b 且a mod b 不為0)
證明:a可以表示成a = kb + r,則r = a mod b
假設d是a,b的一個公約數,則有
d|a,d|b,而r = a - kb,因此d|r
因此d也是(b,a mod b)的公約數
因此(a,b)和(b,a mod b)的公約數是一樣的,其最大公約數也必然相等,得證
或:證明:
第一步:令c=gcd(a,b),則設a=mc,b=nc
第二步:根據前提可知r =a-kb=mc-knc=(m-kn)c
第三步:根據第二步結果可知c也是r的因數
第四步:可以斷定m-kn與n互素【否則,可設m-kn=xd,n=yd,(d>1),則m=kn+xd=kyd+xd=(ky+x)d,則a=mc=(ky+x)dc,b=nc=ycd,故a與b最大公約數≥cd,而非c,與前面結論矛盾】
從而可知gcd(b,r)=c,繼而gcd(a,b)=gcd(b,r),得證。
歐幾里得(GCD)de遞迴寫法:
int gcd(int a,int b) { if(b == 0) return a; else return gcd(b,a%b); } //更簡單寫法 int gcd(int a,int b) { return b?gcd(b,a%b):a;//(或return b==0?a:gcd(b,a%b);) }
由最大公約數又可得最小公倍數(LCM)
lcm(a,b) = (a*b)/gcd(a,b); 程式碼如下:
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
//為了防止a*b超long long 溢位,所以建議下面這種寫法
long long(long long a,long long b)
{
return a/gcd(a,b)*b;
}
gcd lcm 兩個公式的推廣:
gcd(k*a,k*b) = k*gcd(a,b);
lcm(k*a,k*b) = k*lcm(a,b);
//這兩個公式利用
擴充套件歐幾里得演算法
內容:若gcd(a,b) = d,那麼一定存在x,y使得 ax+by = d,這是一個不定方程,一定有多組解,但是隻要找到一組特解x0,y0,就能得到不定方程的通解:
x=x0+b/gcd*k;
y=y0+a/gcd*k;(k為整數)
擴充套件歐幾里得演算法就是在求a,b的最大公約數的同時順帶著把ax+by=d的通解求出來的過程,程式碼與歐幾里得差不多,也是採用的遞迴寫法 : tx=y; ty=x-(a/b)*y(兩個相鄰狀態之間的關係,證明過程不會= =);
//擴充套件歐幾里得
ll exgcd(ll a,ll b,ll &x,ll &y){
//注意x和y必須是引用
if(!b){x=1,y=0;return a;}
int d=exgcd(b,a%b,x,y);
int t=x;x=y;y=t-(a/b)*y;
return d;
}
擴充套件歐幾里得可以判斷不定方程ax+by=c是否有整數解,好像歐幾里得也是可以判斷的-_-||,d=gcd(a,b);如果c可以整除d,那麼不定方程ax+by=c有整數解,否則沒有。
ax+by=c;
d=gcd(a,b);
if(d%c==0)//有整數解
{
x=c/d*x0+b/d*k;
y=c/d*y0-a/d*k;//k為整數
}
else
{
無整數解
}
因為 ax0+by0=gcd
所以 a(x0+b/gcd*t)+b(y0-a/gcd*t)=gcd
所以 x=x0+b/gcd*t, y=y0-a/gcd*t (t為迴圈變數)
擴充套件歐幾里得還能求逆元(*^▽^*)
使用條件: a,b為正整數,而且gcd(a,b) = 1
證明: 因為a,b 互質,所以一定有 ax+by = 1
兩邊同時對b 取餘
ax%b + by %b = 1%b -------> ax%b = 1%b
即 ax ≡ 1 (mod b)
擴充套件歐幾里得中x 就是a關於b的逆元
同理y 就是 b 關於 a的逆元
所以使用完歐幾里得演算法,我們判斷返回值d 是否為1,為1說明 gcd(a,b) = 1
即求得的x就是 a關於b的逆元
void inv(ll a,ll b)
{
ll x,y;
if(exgcd(a,b,x,y) == 1)
cout<<"inv(a):"<<x<<endl;
else
cout<<"不存在逆元"<<endl;
}
UPC 9511 Utawarerumono歐幾里得判斷不定方程是否有整數解,本來以為需要擴充套件歐幾里得,後來發現歐幾里得加暴力就可以,不過要判斷a,b,c取值的多種情況
AC程式碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//ll exgcd(ll a,ll b,ll &x,ll &y){
// //注意x和y必須是引用
// if(!b){x=1,y=0;return a;}
// int d=exgcd(b,a%b,x,y);
// int t=x;x=y,y=t-(a/b)*y;
// return d;//d是a和b的gcd,順便求出來
//}
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
int main()
{
ll t,ans;
ll a,b,c,p1,p2,q1,q2;
scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&p1,&p2,&q1,&q2);
// ll x,y;
ll d=gcd(a,b);
if(a==0&&b==0&&c==0)
{
printf("0\n");
return 0;
}
if(a==0&&b==0&&c!=0)
{
printf("Kuon\n");
return 0;
}
if(a!=0&&b==0)
{
if(c%a!=0)
{
printf("Kuon\n");
}
else
{
t=c/a;
ans=p2*t*t+p1*t;
printf("%lld\n",ans);
}
return 0;
}
if(a==0&&b!=0)
{
if(c%b!=0)
{
printf("Kuon\n");
}
else
{
t=c/b;
ans=q2*t*t+q1*t;
printf("%lld\n",ans);
}
return 0;
}
if(c%d==0)
{
ll minn=1e18;
for(ll i=-1e6;i<=1e6;i++)
{
if((c-a*i)%b==0)
{
ll t=(c-a*i)/b;
ll ans=p2*i*i+p1*i+q2*t*t+q1*t;
minn=min(minn,ans);
}
}
printf("%lld\n",minn);
}
else
printf("Kuon\n");
return 0;
}
HDU 2669 擴充套件歐幾里得裸題
這道題只需要注意兩點:gcd(a,b)=1(這個好辦,求gcd就行了);還有就是x>=0,這就需要用到擴充套件歐幾里得的通解公式了,如果x<0,可以把x一直加b/gcd(a,b),y一直減a/gcd(a,b),直到x>=0為止。
AC程式碼:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll d=exgcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-(a/b)*y;
return d;
}
int main()
{
ll a,b,x,y;
while(cin>>a>>b)
{
ll d=exgcd(a,b,x,y);
if(d==1)
{
while(x<0)
{
x+=b;
y-=a;
}
cout<<x<<" "<<y<<endl;
}
else
cout<<"sorry"<<endl;
}
return 0;
}