1. 程式人生 > >bzoj1477 && exgcd學習筆記

bzoj1477 && exgcd學習筆記

這不 矛盾 mod gcd 周期 現在 class 同余方程 返回

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學習筆記