bzoj1477 && exgcd學習筆記
exgcd
由於忘記了exgcd,這道題就沒做出來。。。
exgcd的用處是求ax+by=gcd(a,b)這樣方程的解
大概是這個樣子的
void ext_gcd(long long a, long long b, long long &x, long long &y) { if(b == 0) { x = 1; y = 0; } else { ext_gcd(b, a % b, y, x); y -= x * (a / b); } }View Code
證明大概是ax+by=gcd(a,b)
根據gcd的性質bx‘+(a%b)y‘=gcd(b,a%b)
這樣就是求出了上面這個方程的解,我們要做的就是通過x‘,y‘求出x,y
怎麽求呢,就是把方程化回ax+by=gcd(a,b)的形式,由於gcd(a,b)=gcd(b,a%b),所以這樣的解是相同的
bx‘+(a-a/b*b)y‘=c (c=gcd(b,a%b))
ay‘+b(x‘-a/b*y‘)=c
那麽就有x=y‘,y=x‘-a/b*y‘
那麽我們就先exgcd(b,a%b,y,x),這樣求出了x‘,y‘我們要返回x,y,由於傳入了y,x,所以x=y‘已經求好了,那麽就是y,現在y=x‘,x=y‘,那麽y=x‘-a/b*y‘,y=y-a/b*x,然後返回就行了
邊界條件是b=0,a=...,這樣就是ax+by=gcd(a,b),ax+0*y=a,那麽自然可以得出x=1,y=0是一組解,順便也求出了gcd
那麽這道題可以列出方程am+y-x=an (mod l),那麽移一下項,得出a(m-n)=x-y (mod l),那麽相當於求(m-n)*a+b*l=x-y 的最小a的解,這不就是exgcd嗎?但是x-y不是gcd(m-n,l),怎麽解決呢?
我們先求出一組解,(m-n)*a‘+b*l=gcd(m-n,l) 設t=gcd(m-n,l)
如果(x-y)%t != 0那麽就沒有解,因為如果有一組解,那麽左邊的式子可以整除t,右邊卻不能,這很明顯矛盾
所以我們對於(m-n)*a‘+b*l=gcd(m-n,l) 設t=gcd(m-n,l)得出來的解,兩邊同乘以(x-y)/t就是原來的那個方程了,那麽把a‘和b‘同時乘以(x-y)/t就得出一組a和b了,但是這不一定是最小的,於是我們要對l/t取模,至於為什麽是l/t,具體是這樣的
我們可以把剛才那個方程看成一個線性同余方程,那麽就是ax=b (mod n)
我們求的是最小解的間隔,這個間隔能幫我們求出最小的正整數解,設這個間隔為d,那麽自然a(x+d)=b (mod n)
和ax=b (mod n)減一下就是 a*d=0 (mod n),那麽我們可以知道a*d是n和a的公倍數,如果想讓d最小,那麽a*d應該等於lcm(a,n),a*d=lcm(a,n),因為lcm(a,n)=a*n/gcd(a,n),那麽就是a*d=a*n/gcd(a,n)
d=n/gcd(a,n),就是剛才那個l/t,所以這個d和l/t就是原方程解的最小周期,取模就能求出最小解
參考http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html
#include<bits/stdc++.h> using namespace std; long long x, y, m, n, l; void ext_gcd(long long a, long long b, long long &x, long long &y) { if(b == 0) { x = 1; y = 0; } else { ext_gcd(b, a % b, y, x); y -= x * (a / b); } } int main() { scanf("%lld%lld%lld%lld%lld", &x, &y, &m, &n, &l); if(m < n) { swap(n, m); swap(x, y); } long long delta_v = m - n, delta_d = ((y - x) % l + l) % l, t = __gcd(delta_v, l), a, b; if(delta_d % t != 0) { puts("Impossible"); return 0; } ext_gcd(delta_v, l, a, b); // printf("a=%lld t=%lld delta_d=%lld\n", a, t, delta_d); l /= t; a *= delta_d / t; a = (a % l + l) % l; printf("%lld", a); return 0; }View Code
bzoj1477 && exgcd學習筆記