【OI】gcd 入门
gcd
定义
\(a,b \in \Z\),记 \(a\) 和 \(b\) 的最大公约数为 \(\gcd(a,b)\),最小公倍数为 \(\operatorname{lcm}(a,b)\)。
约定 \(\gcd(a,0)=a\),\(\gcd(0,0)=0\)。
如何求 \(\gcd(a,b)\)?
欧几里得算法
若 \(d \mid a,b\),则有 \(d \mid a \bmod b\),于是 \(\gcd(a,b)=\gcd(b,a \bmod b)\),称为辗转相除法,可以递归计算。
复杂度为 \(O(\log \max(a,b))\)。
证明:
如果一次运算会使得某个数值减半,则它为对数复杂度。
若 \(a<b\),则 \(\gcd(a,b)=\gcd(b,a)\)。
若 \(a>b\),则 \(a \bmod b\) 至少会让 \(a\) 减半。
得证。
值得注意的是,用欧几里得算法去算斐波那契数列的相邻两项会使复杂度达到最劣。
Stein算法
取模计算速度太慢了,如果换成减法和位运算就会快上很多。
更相减损术:\(\gcd(a,b)=\gcd(b,a-b)\),与辗转相除法同理。
以下为 Stein 算法的过程:
-
若 \(a \bmod 2=1,b \bmod 2=1\),则 \(\gcd(a,b)=\gcd(b,a-b)\)。
-
若 \(a \bmod 2=1,b \bmod 2=0\),则 \(\gcd(a,b)=\gcd(\frac{a}{2},b)\),\(b\) 也同理。
-
若 \(a \bmod 2=0,b \bmod 2=0\),则 \(\gcd(a,b)=2 \gcd(\frac{a}{2},\frac{b}{2})\)。
除法可以用位运算代替。
同理,根据减半原则,时间复杂度为 \(O(\log \max(a,b))\)。
#define ctz __builtin_ctzll
inline int gcd(int a, int b){
if(!a||!b) return a|b;
if(a==1||b==1) return 1;
int az=ctz(a),bz=ctz(b),z=min(az,bz),t;
b>>=bz;
while(a) a>>=az,t=a-b,az=ctz(t),b=min(a,b),a=abs(t);
return b<<z;
}
值得注意的是,用这个可以过基于值域预处理的gcd这道题,可见效率之高。
exgcd
二元线性丢番图方程:形如 \(ax+by=c\) 的方程,其中 \(a,b,c \in \Z\)。
显然,这个方程要么无整数解,要么有无数组整数解。
如何判断这个方程有无整数解?
给出结论:方程有整数解的充要条件是 \(\gcd(a,b) \mid c\)。
证明:
必要性:若方程有整数解,则 \(\gcd(a,b) \mid ax\),\(\gcd(a,b) \mid by\),于是 \(\gcd(a,b) \mid ax+by=c\)。
充分性:
即裴蜀定理,首先考虑方程 \(ax+by=\gcd(a,b)\),需证 \(\exist x,y \in \Z\),使方程成立。
令集合 \(S=\{k \mid ax+by=k,x,y \in \Z\}\),令 \(s\) 为集合的最小正元素,此时 \(ax_0+by_0=s\)。
首先 \(\gcd(a,b) \mid s\)。
其次令 \(a=ps+q,q=a \bmod s \in [0,s)\),则 \(q=a-ps=a(1-px_0)+b(-py_0)\),于是 \(q \in S\),但是 \(s\) 为最小正元素,所以\(q=0\),可得 \(s \mid \gcd(a,b)\)。
综上,\(s=\gcd(a,b)\),此时 \(x=x_0,y=y_0\)。
那么如何求得这个方程的解呢?
首先考虑解 \(ax+by=\gcd(a,b)\)。
构造两个方程:
根据辗转相除法,有:
注意到 \(a \bmod b=a-\lfloor \frac{a}{b} \rfloor b\),
所以:
得 \(x_1=y_2,y_1=x_2-\lfloor \frac{a}{b} \rfloor y_2\)。
这个可以在辗转相除中递归求解,这样我们就得到了一组特解,然后用通解公式得到所有解。
inline int exgcd(int a, int b, int &x, int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(a,b,y,x);
y-=a/b*x;
return d;
}

浙公网安备 33010602011771号