POJ 1061 - 青蛙的約會 - [exgcd求解一元線性同余方程]
先上幹貨:
定理1:
如果d = gcd(a,b),則必能找到正的或負的整數k和l,使ax + by = d.
(參考exgcd:http://www.cnblogs.com/dilthey/p/6804137.html)
定理2:
一元線性同余方程ax ≡ n (mod b) 有解,當且僅當gcd(a,b)|n.
也就是說,解出了ax+by=gcd(a,b),就相當於解出了ax≡n(mod b) (而且只要滿足gcd(a,b)|n,就一定有解)
定理3:
若gcd(a,b) = 1,則方程ax ≡ n (mod b)在[0, b-1]上有唯一解.
由上可知ax ≡ n (mod b) 即方程 ax + by = n,由定理1可知必然存在x,y滿足 ax + by = gcd(a,b) = 1,則必然存在nx,ny滿足 ax + by = n
則必然ax ≡ n (mod b)有解nx,由於nx+kb都是該方程的解,則在[0, b-1]上必然出現一個解。
再證唯一性:
唯一滿足mod b = 0的條件的,只有x1 = x2
定理4:
若gcd(a, b) = d,則方程ax ≡ n (mod b)在[0, b/d - 1]上有唯一解.
這樣,如果我有一個方程ax ≡ n (mod b)的任意解X,那麽我先X mod (b/d),使得其在範圍[-b/d+1,b/d-1]範圍內,
再加上b/d就使其在範圍[1,2(b/d)-1]範圍內,再mod (b/d)就可以使其處在[0,b/d -1]範圍內了。
即在[0,b/d-1]範圍內的解x=[ X mod (b/d) + (b/d) ] mod (b/d).
分割線
然後正式關於題目:
題目鏈接:http://poj.org/problem?id=1061
題解:
青蛙A:a = ( x + mt ) mod L
青蛙B:b = ( y + nt ) mod L
現在要使得a=b,即( x + mt ) = ( y + nt ) (mod L),即( x - y ) + ( m - n ) t = kL;
即求最小的正整數t滿足 ( m - n ) t + KL = y - x (K=-k),即求一元線性同余方程( m - n ) x ≡ ( y - x ) (mod L) 的解;
那麽就像上面那樣,先算( m - n ) * x + L * K = gcd( m - n , L ),得到一個x的值;
然後判斷( y - x ) mod gcd( m - n , L ) == 0 ?,若能整除,則有解;
然後就只要先把x *= ( y - x ) / gcd( m - n , L ) ,再根據上面求出解集中的最小正整數即為答案;
PS.當然,最後一步求[0,b/d-1]範圍內的唯一解時,由於要mod (b/d),我們要保證b/d為正,在本題中,即保證L/d為正,故d要為正。
AC代碼:
1 #include<cstdio> 2 #include<algorithm> 3 typedef long long ll; 4 using namespace std; 5 ll x,y,m,n,L; 6 ll d,ans,K; 7 void exgcd(ll a,ll b,ll &d,ll &x,ll &y){ 8 if(!b){d=a;x=1;y=0;} 9 else {exgcd(b,a%b,d,y,x);y-=(a/b)*x;} 10 } 11 int main() 12 { 13 scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L); 14 if(m<n) 15 { 16 swap(m,n); 17 swap(x,y); 18 } 19 exgcd(m-n,L,d,ans,K); 20 if(d==0 || (x-y)%d!=0) printf("Impossible\n"); 21 else 22 { 23 ans*=(y-x)/d; 24 ans=(ans%(L/d)+(L/d))%(L/d); 25 printf("%I64d\n",ans); 26 } 27 }
PS.曾經剛開始搞ACM的時候,就因為這題是POJ第一頁上為數不多的中文題而鉆了很久,不過以當時的實力,想想就知道怎麽磕都磕不出來的,今天總算把這道題給補上了,感覺還是不錯的
POJ 1061 - 青蛙的約會 - [exgcd求解一元線性同余方程]