Hi everyone!
I'm not a high-rated guy (just ~1300), so this isn't some deep post. I just noticed that gcd and lcm show up in a LOT of problems, and every time I would forget some tiny detail and lose time on it. So I wrote everything in one place. Maybe it helps someone who is at the same stage as me, and if I got something wrong please tell me in the comments, I'll fix it :)
1. Euclid's algorithm
The whole idea is one line:
Unable to parse markup [type=CF_MATHJAX]
$, and when
Unable to parse markup [type=CF_MATHJAX]
$ the answer is
Unable to parse markup [type=CF_MATHJAX]
$.
long long gcd(long long a, long long b) {
while (b) {
a %= b;
swap(a, b);
}
return a;
}
If you use C++17 you can just write std::gcd(a, b) (it's in <numeric>). On older compilers __gcd(a, b) works too.
It runs in
Unable to parse markup [type=CF_MATHJAX]
$. Reason: every two steps the bigger number becomes at least half of what it was. So even for
Unable to parse markup [type=CF_MATHJAX]
$ it's like 90 steps, nothing to worry about.
One small thing:
Unable to parse markup [type=CF_MATHJAX]
$. This is useful because you can start the gcd of an array from 0 and not worry about the first element.
2. LCM (and the overflow trap)
Unable to parse markup [type=CF_MATHJAX]
$
Please divide first, then multiply. If you do a * b / gcd(a, b), the product can overflow even when the final answer would fit. With
Unable to parse markup [type=CF_MATHJAX]
$ the lcm can be up to
Unable to parse markup [type=CF_MATHJAX]
$, which fits in long long but definitely not in int.
long long lcm(long long a, long long b) {
return a / gcd(a, b) * b;
}
Also remember
Unable to parse markup [type=CF_MATHJAX]
$. It's handy more often than you'd think.
3. GCD of an array
long long g = 0;
for (long long x : a) g = gcd(g, x);
A trick that appears a lot: "what is the gcd if I remove one element?" For this, build prefix and suffix gcd arrays:
// pre[i] = gcd(a[0..i]), suf[i] = gcd(a[i..n-1])
// gcd without a[i] = gcd(pre[i-1], suf[i+1])
(be careful with the borders, i = 0 and i = n-1. This is where I usually get WA.)
4. Some properties worth remembering
Unable to parse markup [type=CF_MATHJAX]
$, so
Unable to parse markup [type=CF_MATHJAX]
$ always divides
Unable to parse markup [type=CF_MATHJAX]
$. -
Unable to parse markup [type=CF_MATHJAX]
$ divides
Unable to parse markup [type=CF_MATHJAX]
$ for any
Unable to parse markup [type=CF_MATHJAX]
$. This helps in problems where you add the same number to two values and want them to share a factor. - If
Unable to parse markup [type=CF_MATHJAX]
$, you can write
Unable to parse markup [type=CF_MATHJAX]
$,
Unable to parse markup [type=CF_MATHJAX]
$ with
Unable to parse markup [type=CF_MATHJAX]
$. A lot of problems become much easier after this substitution. - While you go through an array, the prefix gcd changes at most
Unable to parse markup [type=CF_MATHJAX]
$ times, since every time it changes it becomes at most half of the previous value.
5. Extended Euclid
It finds
Unable to parse markup [type=CF_MATHJAX]
$ such that
Unable to parse markup [type=CF_MATHJAX]
$.
long long exgcd(long long a, long long b, long long &x, long long &y) {
if (b == 0) {
x = 1; y = 0;
return a;
}
long long x1, y1;
long long g = exgcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return g;
}
Where it's useful: - The equation
Unable to parse markup [type=CF_MATHJAX]
$ has an integer solution if and only if
Unable to parse markup [type=CF_MATHJAX]
$ divides
Unable to parse markup [type=CF_MATHJAX]
$. - Modular inverse of
Unable to parse markup [type=CF_MATHJAX]
$ modulo
Unable to parse markup [type=CF_MATHJAX]
$ when
Unable to parse markup [type=CF_MATHJAX]
$: run exgcd(a, m, x, y) and take
Unable to parse markup [type=CF_MATHJAX]
$ modulo
Unable to parse markup [type=CF_MATHJAX]
$ (make it positive!). This works even when
Unable to parse markup [type=CF_MATHJAX]
$ is not prime, unlike Fermat's trick.
6. Mistakes I (and probably you) make
- Using
intfor lcm and getting overflow. - Multiplying before dividing.
- Forgetting that
Unable to parse markup [type=CF_MATHJAX]
$, and then dividing by it. - Forgetting that the lcm of a whole array grows really fast, so it may not fit in long long after a few elements.
7. Problems to practice
Easy ones to start with: - 1325A - EhAb AnD gCd EhAb AnD gCd - 1498A - GCD Sum GCD Sum
After that, go to the problemset, filter by the tag "number theory" and rating 800 to 1500, and solve a few. I'm still doing this myself.
That's all, thanks for reading!
Question for you: what is the most interesting or surprising problem you have seen that uses gcd? I would like to add it to the list. Also if you know a nicer way to explain any part here, please share.



