|
| 1 | +--- |
| 2 | +categories: |
| 3 | +- OI |
| 4 | +title: 快速 GCD 算法 |
| 5 | +date: 2024-07-25 17:16:27 |
| 6 | +tags: |
| 7 | +- math |
| 8 | +--- |
| 9 | + |
| 10 | +> 例题:[Luogu P5435](https://www.luogu.com.cn/problem/P5435) |
| 11 | +
|
| 12 | +## 预处理 GCD |
| 13 | + |
| 14 | +一种 `$O(n)$` 预处理,`$O(1)$` 查询两个小于 `$n$` 的数的快速 `$\gcd$`。 |
| 15 | + |
| 16 | +### 引理 |
| 17 | + |
| 18 | +对于任意正整数 `$n$`,可以将 `$n$` 写成 `$n=abc$`,满足 `$a,b,c$` 任意一个小于 `$\sqrt{n}$` 或为质数。 |
| 19 | + |
| 20 | +考虑归纳,首先对于 `$1$`,显然成立。 |
| 21 | + |
| 22 | +否则,设 `$n$` 的最小质因子为 `$p$`,设 `$\dfrac{n}{p}=xyz$` 是一个合法表示,不妨设 `$x\le y\le z$`。若 `$x=1$`,则 `$n=pyz$` 是一个合法表示。不然有 `$p\le x\le y\le z$`,则 `$p^4\le n$`。若 `$xp\gt\sqrt{n}$`,有 `$yp\gt\sqrt{n},zp\gt\sqrt{n}$`,则 `$xyzp^3\gt n^{3/2}$`,矛盾,因此 `$n=(xp)yz$` 是合法表示。 |
| 23 | + |
| 24 | +注意这个证明给出了合法表示的构造方法。 |
| 25 | + |
| 26 | +### 预处理 |
| 27 | + |
| 28 | ++ 预处理所有 `$(i,j)$`,其中 `$i,j\le \sqrt{n}$` 的 `$\gcd$`。显然可以 `$O(n)$` 推出来。 |
| 29 | ++ 预处理 `$n$` 以内质数。 |
| 30 | + |
| 31 | +### 查询 |
| 32 | + |
| 33 | +计算 `$\gcd(x,y)$`。 |
| 34 | + |
| 35 | +设 `$x=abc$`,则: |
| 36 | + |
| 37 | +$$(x,y)=(a,y)\times(b,\dfrac{y}{(a,y)})\times(c,\dfrac{y}{(ab,y)})$$ |
| 38 | + |
| 39 | +若 `$a\in\mathbf{P}$`,只需判断 `$a$` 是否整除 `$y$`,否则 `$(a,y)=(y\bmod a,a)$`,因为 `$a\le\sqrt{n}$`,可以查表。 |
| 40 | + |
| 41 | +### 实现 |
| 42 | + |
| 43 | +```cpp |
| 44 | +const int T = 1000; |
| 45 | +const int M = 1000000; |
| 46 | + |
| 47 | +int g[T][T], fac[M][3]; |
| 48 | +bitset<M> vis; |
| 49 | +vector<int> pri; |
| 50 | +void prework() { |
| 51 | + vis[0] = vis[1] = true; |
| 52 | + fac[1][0] = fac[1][1] = fac[1][2] = 1; |
| 53 | + for (int i = 2; i < M; ++i) { |
| 54 | + if (!vis[i]) { |
| 55 | + fac[i][0] = fac[i][1] = 1; |
| 56 | + fac[i][2] = i; |
| 57 | + pri.push_back(i); |
| 58 | + } |
| 59 | + for (int j : pri) { |
| 60 | + int mul = i * j; |
| 61 | + vis[mul] = true; |
| 62 | + fac[mul][0] = fac[i][0] * j; |
| 63 | + fac[mul][1] = fac[i][1]; |
| 64 | + fac[mul][2] = fac[i][2]; |
| 65 | + sort(fac[mul], fac[mul] + 3); |
| 66 | + if (i % j == 0) |
| 67 | + break; |
| 68 | + } |
| 69 | + } |
| 70 | + for (int i = 0; i < T; ++i) |
| 71 | + g[0][i] = g[i][0] = i; |
| 72 | + for (int i = 1; i < T; ++i) |
| 73 | + for (int j = 1; j <= i; ++j) |
| 74 | + g[i][j] = g[j][i] = g[j][i % j]; |
| 75 | +} |
| 76 | +int gcd(int a, int b) { |
| 77 | + int ans = 1; |
| 78 | + for (int i = 0; i < 3; ++i) { |
| 79 | + int _ = fac[a][i] > T ? (b % fac[a][i] ? 1 : fac[a][i]) |
| 80 | + : g[fac[a][i]][b % fac[a][i]]; |
| 81 | + b /= _; |
| 82 | + ans *= _; |
| 83 | + } |
| 84 | + return ans; |
| 85 | +} |
| 86 | +``` |
| 87 | +
|
| 88 | +## 更相减损术 |
| 89 | +
|
| 90 | +小常数 `$O(\log n)$`。 |
| 91 | +
|
| 92 | ++ 若 `$a=b$`,`$(a,b)=a=b$` |
| 93 | ++ 若 `$2\mid a,2\mid b$`,`$(a,b)=\left(\dfrac{a}{2},\dfrac{b}{2}\right)$`。 |
| 94 | ++ 若 `$2\mid a,2\nmid b$`(反之同理),`$(a,b)=\left(\dfrac{a}{2},b\right)$`。 |
| 95 | ++ 若以上均不满足,设 `$a>b$`,则 `$(a,b)=(a-b,b)$`。 |
| 96 | +
|
| 97 | +```cpp |
| 98 | +int gcd(int a, int b) { |
| 99 | + int i = __builtin_ctz(a); |
| 100 | + int j = __builtin_ctz(b); |
| 101 | + int k = min(i, j); |
| 102 | + int d; |
| 103 | + b >>= j; |
| 104 | + while (a) { |
| 105 | + a >>= i; |
| 106 | + d = b - a; |
| 107 | + i = __builtin_ctz(d); |
| 108 | + if (a < b) b = a; |
| 109 | + if (d < 0) a = -d; |
| 110 | + else a = d; |
| 111 | + } |
| 112 | + return b << k; |
| 113 | +} |
| 114 | +``` |
0 commit comments