【校OJ】擴充套件gcd-時間複雜性 (10分)
GCD
gcd就是最大公約數的意思
我們首先要了解擴充套件gcd,首先要知道gcd,也就是歐幾里得演算法
即gcd(a,b) = gcd(b, a mod b) a,b的最大公因數 = b,a mod b的最大公因數
證明如下:
a = kb + r 則 r = a mod b
假設d 為 a b 的最大公約數(d = gcd(a,b))
那麼d 也是 r 的最大公約數(因為 r = a - kbd既可以被a除斷,也可以被b除斷,所以也能被r除斷)
即d 是 a mod b的最大公約數(d = gcd(b,a mod b))
從另一頭開始證明
假設d 為 b a mod b的最大公約數(d = gcd(b,a mod b))
a = kb + r,d也是a的最大公約數
所以d = gcd(a, b)
得證
並且當b == 0時,a就是a,b的最大公約數(這個我也不造怎麼證明,就記下來叭)
有模板如下
int gcd(int a, int b){
return b == 0 ? a : gcd(b, a % b);
}
擴充套件GCD
擴充套件gcd不僅可以求出ab的最大公約數,還可以求出ax + by = gcd(a,b)方程的解
有結論如下x1 = y2, y1 = x2 - (a / b * b)y2
為啥呢?
證明如下:
我們由歐幾里得演算法可得gcd(a,b) = gcd(b,a % b),那麼有下面三個等式
ax1 + by1 = gcd(a,b)……(1)
bx2 + (a % b)y2 = gcd(b,a%b)……(2)
(1)= (2)
=> ax1 + by1 = bx2 + (a - (a / b) * b)y2 = ay2 + b(x2 - (a / b)* y2)
所以:
x1 = y2
y1 = x2 - (a / b * b)y2 證畢
並且b == 0時 x = 1 y = 0(just remember this)//d=a,x=1,y=0,此時等式d=ax+by成立
有模板如下
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 xt = x; x = y; y = xt - a / b * y; return d; }
推廣
如果我們要求ax + by = n的解呢?
也可以用擴充套件gcd,求出gcd(a,b)之後,如果n不能被gcd(a,b)整除,就是無解的
如果可以被整除,我們可以得到一組特解x0,y0
由特解可以得到通解的通項公式(為什麼成立?直接帶到ax + by = gcd(a,b)裡試試)
x = x0 + b/gcd(a,b) * t
y = y0 - a / gcd(a,b) * t(t是一個整數)
通常題目需要我們求出方程組最小解
在這裡b/gcd(a,b),a/gcd(a,b)分別是x,y的最小區間,也就是說
x在0 ~ b/gcd(a,b)上只有一個整數解
y在0 ~ a/gcd(a,b)上只有一個整數解
那麼這個解就是我們要求的最小整數解
為什麼最小區間是b / gcd(a,b)?
證明如下:
設有對特解為x0,y0,x的最小區間為c,y的最小區間為d,則有
x = x0 + ct
y = y0 - dt
帶入ax + by = n
ax + by
= a (x0 + ct) + b (y0 - dt)
= ax0 + by0 + act - bdt = n
因為ax0 + by0 = n
所以act - bdt = 0
所以ac = bd,想要用最小的c和d讓式子成立,我們就得讓ac = bd = ab / gcd(a,b)(其實這個地方思路有點跳,我也不太懂)
可得c = b / gcd(a,b), d = a / gcd(a,b),證畢
所以,最後求得的x = (x0 % (b / gcd(a,b)) + b / gcd(a,b))%(b / gcd(a,b))
x0%(b / gcd(a,b))讓x的範圍落在- b / gcd(a,b) ~ + b / gcd(a,b)上 再加上一個 b / gcd(a,b),區間落在 0 ~ 2(b / gcd(a,b))上
再取模b/gcd(a,b)區間就落在0 ~ b / gcd(a,b)就是我們要求的最小值
下面來看一道題目:POJ2115
644.擴充套件gcd-時間複雜性 (10分)
C時間限制:3000 毫秒 | C記憶體限制:3000 Kb
題目內容:
計算迴圈語句的執行頻次 for(i=A; i!=B ; i+=C) x+=1;
其中A,B,C,i都是k位無符號整數。
輸入描述
A B C k, 其中0<k<32
輸出描述
輸出執行頻次數,如果是無窮,則輸出“forever”
輸入樣例
3 7 2 16
輸出樣例
2
題目要求(a + xc ) % 2 ^ k 等不等於b(t是任意正整數)
若有解有表示式(a + xc)mod 2^k == b
那麼a + xc - b = y * 2^k(y是任意正整數)
cx - 2^k * y = b - a
是不是很像
ax + by = n
我們就把這些數字帶進去用exgcd做就可以啦(~ ̄▽ ̄)~
程式碼如下:
//POJ2115
#include <iostream>
#include <cstdio>
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 xt = x;
x = y;
y = xt - a / b * y;
return d;
}
int main(){
LL A,B,C,k;
while(scanf("%lld%lld%lld%lld",&A,&B,&C,&k)){
if(A == 0 && B == 0 && C == 0 && k == 0){
break;
}
LL a = C;
LL b = (LL)1 << k;
LL n = B - A;
LL x,y;
LL d = exgcd(a,b,x,y);
if(n % d){
cout<<"FOREVER"<<endl;
}else{
//x = (x0 % (b / gcd(a,b)) + b / gcd(a,b)) % b / gcd(a,b)
x = (x * (n / d))%b;
x = (x % (b / d) + (b / d)) % (b / d);
cout<<x<<endl;
}
}
return 0;
}