int gcd(int a,int b) //最大公約數 { if(b==0) return a; else return gcd(b,a%b); } int lcm(int a,int b) //最小公倍數 { return a/gcd(a,b)*b; //防止溢出 }
2.擴展歐幾里得
2.1定義:
ax+by=gcd(a,b)=d,在已知a,b的狀況下求解出一組x,yhtml
2.2推導過程:
由於a%b=a-(a/b)b, 則有 d=bx1+[a-(a/b)b]y1=bx1+ay1-(a/b)by1=ay1+b(x1-a/by1), 故 x=y1,y=x1-a/by1ios
2.3性質:
1.若經過擴展歐幾里得求出一組特解(x0,y0),那麼有ax0+by0=d.ide
則方程的通解爲,其中k爲任意整數 x0=d/exgcd()*x,y0=d/exgcd()*y;
x=x0+k*(b/d)--------x = x0 +( b / gcd( a, b ) ) * k
y=y0-k*(a/d)--------y = y0 - ( a / gcd( a, b ) ) * k
2.已知ax+by=d的解,對於ax+by=c的解,c爲任意正整數,只有當d|c時纔有解
其通解爲
x=(c/d)x0+k(b/d)
y=(c/d)y0-k(a/d)函數
//擴展歐幾里得處理ax+by=gcd(a,b)=d解的問題 int exgcd(int a,int b,int &x,int &y) //獲得gcd(a,b)的值 { if(b==0) { x=1,y=0; return a; } int r=exgcd(b,a%b,x,y); int t=x; x=y; y=t-a/b*y; return r;//gcd(a,b) }
2.4常見用法:測試
(1)求形如ax+by=c的通解,或從中選取某些特解
(2)求乘法逆元
(3)求解線性同餘方程spa
寫的超級好的博客:http://www.cnblogs.com/wkfvawl/p/9350867.html.net
解題思路:code
1.先化成不定方程ax+by=c orm
2.a,b參數取正數,判斷a,b是否爲負數,爲負數就a,b,c都乘上-1,另外注意若a,b中只有一個是負數,那另外一個不須要乘-1,由於a,b自己是含未知數參數的。htm
3.r=exgcd(a,b,x,y),而後判斷是否有解採用if(c%r==0)
求特解:x0=x*c/r, y0=y*c/r;
求通解:x=x0+b/r*c , y=y0-a/r*c (其中 t 爲整數)
求最小解:int s=b/r; minx= (x0%s+s)%s;//最小解
2.4.1例題實例:
問題描述:對於 ax+by=c 的不定方程求通解或特解?
設 r=gcd(a,b),
若 c%r!=0 這此方程無整數解
若 c%r==0,特解爲x1=x0*c/r , y1=y0*c/r,通解 x=x1+b/r*t , y=y1-a/r*t (其中 t 爲整數)
不定方程的最小解:
r=exgcd(a,b,x,y);
x=x*c/r;
int s=b/r;
minx= (x%s+s)%s;//最小解
例題1:
題目連接:https://cn.vjudge.net/problem/HDU-2669
題目大意:0<a, b<=2^31,X>=0,給定a,b求知足X*a + Y*b = 1的最小正數X,以及與之對應的Y。
解題思路:求形如ax+by=c的最小解,x=x0+k*(b/d), y=y0-k*(a/d),從通解可看出,其中b / d 都正數,x 和 t成正比,當x0爲正數的時候 t == 0,有最小值;當x0爲負數的時候,x最小就是0,與橫軸相交,因此能夠求出 x0 + b / t * t = 0時候的 t,由於此時 t是整數,不必定是準確相交的那一個點,因此當前的x多是負數,若是爲負數,t++,取下一個整數點。
ac代碼:
1 #include<iostream> 2 using namespace std; 3 //擴展歐幾里得處理ax+by=gcd(a,b)=d解的問題 4 int exgcd(int a,int b,int &x,int &y) //獲得gcd(a,b)的值 5 { 6 if(b==0) 7 { 8 x=1,y=0; 9 return a; 10 } 11 int r=exgcd(b,a%b,x,y); 12 int t=x; x=y; y=t-a/b*y; 13 return r;//gcd(a,b) 14 } 15 int main() 16 { 17 int a,b; 18 while(~scanf("%d%d",&a,&b)) 19 { 20 int x,y; 21 int d=exgcd(a,b,x,y); 22 if(1%d)//有餘數 23 { 24 cout<<"sorry"<<endl; 25 continue; 26 } 27 else 28 { 29 //經過exgcd函數獲得特解 30 x=x/d,y=y/d; 31 if(x>0)//因爲a,b爲正數,因此k=0取最小 32 cout<<x<<" "<<y<<endl; 33 else 34 {//當x0爲負數的時候,x最小就是0,與橫軸相交, 但x多是負數,若是爲負數,取下一個整數點 35 int k=(-1)*x/b*d; 36 int kx=x+b/d*k; 37 int ky=y-k*(a/d); 38 if(kx<0) 39 { 40 kx=x+b/d*(k+1); 41 ky=y-(k+1)*(a/d); 42 } 43 cout<<kx<<" "<<ky<<endl; 44 } 45 } 46 } 47 return 0; 48 }
例題2:
題目連接:https://cn.vjudge.net/problem/ZOJ-3593
題意:一我的要從A走到B 只能走a布、b步、(a+b)步,能夠往左或右走, 問 最少要走幾步,不能走到的輸出-1
題目思路:能夠設走x步a米的,走y部b米的,獲得式子:ax + by = B - A;
經過擴展歐幾里德可得x和y
x、y的解集:X = x + b/gcd(a,b) * t; Y = y - a /gcd(a,b) * t;
x與y最接近時爲答案,這樣的話c能夠取的更多,那麼步數會最小
它們
(1)X、Y同號時,即方向相同,能夠走a+b來減小步數,取max(X,Y)(這裏能夠畫X、Y的座標圖來幫助理解)
(2)X、Y異號時,即方向相反,步數絕對值相加,abs(X) + abs(Y)
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 const long long maxn = 0x3f3f3f3f; 6 //#define maxn 0x3f3f3f3f 7 long long exgcd(long long a,long long b,long long &x,long long &y) 8 { 9 if(b==0) 10 { 11 x=1,y=0; 12 return a; 13 } 14 long long r=exgcd(b,a%b,x,y); 15 long long t=x;x=y;y=t-a/b*y; 16 return r; 17 } 18 long long Abs(long long a) 19 { 20 if(a<0) 21 a=-1*a; 22 return a; 23 } 24 int main() 25 { 26 long long a,b,A,B; 27 long long ans; 28 int T; 29 cin>>T; 30 while(T--) 31 { 32 cin>>A>>B>>a>>b; 33 long long x,y; 34 long long h=exgcd(a,b,x,y); 35 if((B-A)%h!=0) 36 printf("-1\n"); 37 else{ 38 x=(B-A)/h*x;//註釋修改 39 y=(B-A)/h*y;//註釋修改 40 long long minx,miny; 41 long long k; 42 b=b/h; 43 a=a/h; 44 k=(y-x)/(b+a);//註釋修改 45 ans=maxn*maxn; //註釋修改 這裏定義成maxn數據定義小了 46 for(int i=k-1;i<=k+1;i++) 47 { 48 minx=x+b*i,miny=y-a*i; 49 long long m; 50 if(Abs(minx+miny)==Abs(minx)+Abs(miny))//說明x與y同號 51 m=max(Abs(minx),Abs(miny)); 52 else 53 m=Abs(minx)+Abs(miny); 54 if(m<ans) 55 ans=m; 56 // if((minx>0&&miny>0)||(minx<0&&miny<0))//x與y同號 57 // { 58 // m=Abs(min(minx,miny))+Abs(minx-miny);//這句有問題的 測試這個存在數據Abs(min(minx,miny))+Abs(minx-miny)!=max(Abs(minx),Abs(miny)); 59 // } 60 // else 61 // m=Abs(minx)+Abs(miny); 62 // if(m<ans) 63 // ans=m; 64 65 } 66 cout<<ans<<endl; 67 } 68 } 69 }
wa的心得或者能夠說是經驗:
1.maxn定義不一樣而wa:
const long long maxn = 0x3f3f3f3f; //正確
//#define maxn 0x3f3f3f3f 錯誤
2.除法運算要考慮是否倍數除,能夠倍數除最好倍數除
x=(B-A)/h*x; y=(B-A)/h*y;//正確
x=x/h*(B-A); y=y/h*(B-A); //錯誤
3.ans=maxn*maxn; //註釋修改 這裏定義成maxn數據定義小了
3.前一個爲正確寫法,後一個爲錯誤寫法
分析:二者的不一樣在於在for循環裏面minx,miny,後一種不能保證i=k-1~k+1這裏面取得答案,由於多了一個參數。
1 b=b/h; 2 a=a/h; 3 k=(y-x)/(b+a);//註釋修改 4 for(int i=k-1;i<=k+1;i++) 5 minx=x+b*i,miny=y-a*i;
1 b=b; 2 a=a; 3 k=(y-x)/(b+a)*h; 4 for(int i=k-1;i<=k+1;i++) 5 minx=x+b*i/h,miny=y-a*i/h;
2.4.2例題實例:求乘法逆元
問題描述:求解一個數 a 對於另外一個數 m 的乘法逆元:a*x=1(mod m) 也即:a*x%m=1求x 也即a*x+m*y=1
當gcd(a , m) != 1 ,a*x + m*y = 1沒有解
當1 % gcd(a , m) == 0,a*x + m*y = 1有解
x0=x*1/exgcd();
最小的解:x0 % m
x 的通解: x0 + m*t
#include<iostream> #include<cmath> using namespace std; //擴展歐幾里得處理ax+by=gcd(a,b)=d解的問題 int exgcd(int a,int b,int &x,int &y) //獲得gcd(a,b)的值 { if(b==0) { x=1,y=0; return a; } int r=exgcd(b,a%b,x,y); int t=x; x=y; y=t-a/b*y; return r;//gcd(a,b) } //求解一個數 a 對於另外一個數 m 的乘法逆元:a*x=1(mod m) 也即:a*x%m=1求x 也即a*x+m*y=1 int inversenum(int a,int m) { int x,y,ans,gcd; gcd=exgcd(a,m,x,y); if(1%gcd!=0)//無解 return -1; x=x*1/gcd; m=abs(m); ans=x%m; if(ans<=0) ans+=m; return ans; } int main() { int a,m; cin>>a>>m; cout<<inversenum(a,m)<<endl; }
1 #include<iostream> 2 #include<cmath> 3 using namespace std; 4 //擴展歐幾里得處理ax+by=gcd(a,b)=d解的問題 5 int exgcd(int a,int b,int &x,int &y) //獲得gcd(a,b)的值 6 { 7 if(b==0) 8 { 9 x=1,y=0; 10 return a; 11 } 12 int r=exgcd(b,a%b,x,y); 13 int t=x; x=y; y=t-a/b*y; 14 return r;//gcd(a,b) 15 } 16 //求解一個數 a 對於另外一個數 m 的乘法逆元:a*x=1(mod m) 也即:a*x%m=1求x 也即a*x+m*y=1 17 int inversenum(int a,int m) 18 { 19 int x,y,ans,gcd; 20 gcd=exgcd(a,m,x,y); 21 if(1%gcd!=0)//無解 22 return -1; 23 x=x*1/gcd; 24 m=abs(m); 25 ans=x%m; 26 if(ans<=0) 27 ans+=m; 28 return ans; 29 } 30 int main() 31 { 32 int T; 33 cin>>T; 34 while(T--){ 35 int a,m; 36 cin>>a>>m; 37 if(inversenum(a,m)==-1) 38 cout<<"Not Exist"<<endl; 39 else 40 cout<<inversenum(a,m)<<endl; 41 } 42 }
2.4.2例題實例:求線性同餘方程
問題描述:關於 x 的模方程 ax%b=c (或者是(x+mt)%L=(y+nt)%L)的解,方程轉換爲 ax+by=c 其中 y 通常爲非正整數
解得 x1=x0*c/r,通解爲 x=x1+b/r*t
設 s=b/r (已證實 b/r 爲通解的最小間隔),則 x 的最小正整數解爲 (x1%s+s)%s
例題:poj 1061
根據題意:可寫出(x+mt)%L=(y+nt)%L,求可取的最小t
(m-n)*t=(y-x)%L; (m-n)*t+L*b=y-x; (m-n)*a+L*b=y-x; 當作a*m1+b*n1=t;(m1=m-n,n1=L,t=y-x)
注意:數據類型應該爲long long
1 #include<iostream> 2 #include<cmath> 3 using namespace std; 4 //擴展歐幾里得處理ax+by=gcd(a,b)=d解的問題 5 long long t; 6 long long exgcd(long long a,long long b,long long &x,long long &y) //獲得gcd(a,b)的值 7 { 8 if(b==0) 9 { 10 x=1,y=0; 11 return a; 12 } 13 long long r=exgcd(b,a%b,x,y); 14 long long t=x; x=y; y=t-a/b*y; 15 return r;//gcd(a,b) 16 } 17 long long solve(long long m,long long n) 18 { 19 long long x,y; 20 long long r=exgcd(m,n,x,y); 21 if(t%r) 22 { 23 return -1; 24 } 25 x=x*t/r; 26 long long s=n/r; 27 return (x%s+s)%s;//不定方程的最小解x 28 } 29 int main() 30 { 31 long long x,y,m,n,L; 32 cin>>x>>y>>m>>n>>L; 33 //不定式a*(m-n)+b*L=y-x; 當作a*m+b*n=t; 34 t=y-x; 35 m=m-n; 36 n=L; 37 if(m<0)//整個不定式a,b取正數,因爲b>0,b不須要變(b前面有係數),因此t須要改變 38 { 39 m=-1*m; 40 t=-1*t; 41 } 42 if(solve(m,n)==-1) 43 cout<<"Impossible"<<endl; 44 else 45 { 46 cout<<solve(m,n)<<endl; 47 } 48 return 0; 49 }
例題:p2421荒島野人
題目連接:https://www.luogu.org/problemnew/show/P2421
題目思路:暴力+exgcd
首先判斷兩個野人是否會在同一個洞穴:(Ci+Pi*t)%M==(Cj+Pj*t)%M (p[i]-p[j])*t=(C[j]-C[i])%M (P[i]-P[j])*a+M*b=C[j]-C[i]
暴力求M,從1遞增,知足條件就停下來,check(M)
check(M)的做用是判斷n個野人之間沒有住在同一個洞穴,即餘數不一樣或者是相同餘數數字大於兩野人最小壽命。
題目代碼:
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 int c[200],p[200],l[200],n; 5 int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);} 6 //擴展歐幾里得處理ax+by=gcd(a,b)=d解的問題 7 int exgcd(int a,int b,int &x,int &y) //獲得gcd(a,b)的值 8 { 9 if(b==0) 10 { 11 x=1,y=0; 12 return a; 13 } 14 int r=exgcd(b,a%b,x,y); 15 int t=x; x=y; y=t-a/b*y; 16 return r;//gcd(a,b) 17 } 18 bool go(int i,int j,int b){ 19 int a=p[i]-p[j],f=c[j]-c[i],g,x,y; 20 if(a<0) 21 { 22 a=a*-1,f=-1*f; 23 } 24 g=exgcd(a,b,x,y); 25 if(f%g==0){ 26 x=x*f/g; 27 b=b/g; 28 x=(x%b+b)%b;//最小解 29 if(x<=min(l[i],l[j])) 30 return 0; 31 } 32 return 1; 33 } 34 bool check(int m){ 35 int i,j,k; 36 for(i=1;i<n;i++) 37 for(j=i+1;j<=n;j++) 38 if(!go(i,j,m))return 0; 39 return 1; 40 } 41 int main() 42 { int m=0,i,j,k; 43 scanf("%d",&n); 44 for(i=1;i<=n;i++)scanf("%d%d%d",&c[i],&p[i],&l[i]),m=max(m,c[i]); 45 while(m){ 46 if(check(m)){ 47 printf("%d\n",m); 48 return 0; 49 } 50 m++; 51 } 52 return 0; 53 }
題目集應用:http://www.cnblogs.com/yzxverygood/category/1232095.html
第二題:hdu 1576 https://cn.vjudge.net/problem/HDU-1576
題目思路:求(A/B)%9973,已知條件有:n=A%9973,gcd(B,9973)=1,A%B=0;輸入n,B,求(A/B)%9973 的值?
設(A/B)%9973=x,即(A/B)=9973h+x; A=9973*h*B+x*B ; n=A%9973即n=(9973*h*B+x*B)%9973,即n=(x*B)%9973,即x*B-9973y=n;求最小的正數x
1 #include<iostream> 2 using namespace std; 3 //擴展歐幾里得處理ax+by=gcd(a,b)=d解的問題 4 int exgcd(int a,int b,int &x,int &y) //獲得gcd(a,b)的值 5 { 6 if(b==0) 7 { 8 x=1,y=0; 9 return a; 10 } 11 int r=exgcd(b,a%b,x,y); 12 int t=x; x=y; y=t-a/b*y; 13 return r;//gcd(a,b) 14 } 15 int main() 16 { 17 int T,n,B; 18 scanf("%d",&T); 19 while(T--) 20 { 21 scanf("%d%d",&n,&B); 22 int x,y; 23 int d=exgcd(B,9973,x,y); 24 x=x*n/d;//最小解 25 int s=9973/d; 26 x=(x%s+s)%s; 27 cout<<x<<endl; 28 } 29 return 0; 30 }
第三題:UVA12169 https://cn.vjudge.net/problem/UVA-12169
題目大意:有3個整數 x[1], a, b 知足遞推式x[i]=(a*x[i-1]+b)mod 10001。由這個遞推式計算出了長度爲2T的數列,如今要求輸入x[1],x[3],......x[2T-1], 輸出x[2],x[4]......x[2T]. T<=100,0<=x<=10000. 若是有多種可能的輸出,任意輸出一個結果便可。
題目思路:第一種直接暴力方法,枚舉a,b,找到合適的a,b(即知足式子,又由於咱們只知道x[1],x[3]..,因此只要它知足全部的x[i]=(a*((a*x[i-2]+b)mod 10001)+b)mod 10001)而後輸出相對於應的x[2],x[4]...
ac代碼:
1 #include<iostream> 2 using namespace std; 3 #define maxn 100005 4 int a[maxn]; 5 int main() 6 { 7 int n; 8 scanf("%d",&n); 9 for(int i=0;i<n;i++) 10 scanf("%d",&a[i]); 11 int flag=0; 12 for(int i=1;i<=10000;i++) 13 { 14 for(int j=1;j<=10000;j++) 15 { 16 flag=0;//i,j爲a,b 篩選知足式子的a,b 17 for(int h=1;h<n;h++) 18 { 19 if(a[h]!=((i*((i*a[h-1]+j)%10001)+j)%10001)) 20 { 21 flag=1; 22 break; 23 } 24 } 25 if(flag==0) 26 { 27 for(int h=0;h<n;h++) 28 { 29 printf("%d\n",(i*a[h]+j)%10001); 30 } 31 break; 32 } 33 } 34 if(flag==0) 35 break; 36 } 37 return 0; 38 }
第二種方法,比第一種方法快,咱們枚舉a,利用簡化式子(a+1)*b mod 10001 = (x[3]-a*a*x[1]) mod 10001。這裏就變成了同模方程,擴展歐幾里得便可解答,求b。而後由a,b求相對於應的x[2],x[4]...
相應博客:http://www.javashuo.com/article/p-dbpklgwp-hu.html