CF 102697037 - Practice Katanas

The problem asks for the least common multiple, or LCM, of two positive integers a and b. The LCM is the smallest positive integer that is divisible by both numbers.

CF 102697037 - Practice Katanas

Rating: -
Tags: -
Solve time: 47s
Verified: yes

Solution

Problem Understanding

The problem asks for the least common multiple, or LCM, of two positive integers a and b. The LCM is the smallest positive integer that is divisible by both numbers. For example, the LCM of 2 and 4 is 4, because 4 is divisible by both and no smaller positive integer has that property.

The input contains the two positive integers a and b. The output is their LCM as a single integer. The official statement does not give an explicit numerical upper bound for a and b, but the intended solution uses the Euclidean algorithm for the GCD, whose running time is logarithmic in the input values. That makes it suitable even when the integers are much larger than the small values shown in the examples. A solution that scans possible multiples or repeatedly tests divisibility can require a number of operations proportional to the answer itself, which can be vastly larger than the input size.

There are several small cases where an implementation based on a naive formula can go wrong. If one number already divides the other, the LCM is the larger number. For input 2 4, the correct output is 4. A method that starts searching from a * b without reducing by the common factor wastes work, while an implementation that accidentally searches only strict multiples of both can miss the answer.

When the numbers are equal, the LCM is that same number. For input 7 7, the correct output is 7. A careless implementation based on generating multiples could unnecessarily search for a second occurrence rather than recognizing that the first number is already a common multiple.

The numbers can also be coprime. For input 3 5, the correct output is 15. In this case the GCD is 1, so there is no common factor to remove from the product. An implementation that assumes there is always a non-trivial GCD would produce an incorrect result.

The order of the two inputs must not matter. For 4 6 and 6 4, the LCM is 12 in both cases. The Euclidean algorithm handles either ordering naturally, so there is no need to manually swap the inputs before computing the GCD.

Approaches

A direct approach is to search through multiples until finding one divisible by both numbers. We could start at the larger of a and b, test whether it is divisible by both, then continue with the next integer until a common multiple is found. This is correct because the first common multiple encountered is precisely the LCM. However, in the worst case the search can require Θ(LCM(a, b)) divisibility checks. For coprime numbers, the LCM is a * b, so the worst case can require Θ(a * b) operations. Even for moderately large inputs this is impractical.

Another direct mathematical approach is to factor both numbers and construct the LCM from their prime factors. This works, but factoring requires substantially more work than necessary. The problem already gives us the key relationship between GCD and LCM:

LCM(a, b) = a * b / GCD(a, b).

The remaining task is to calculate the GCD efficiently.

The Euclidean algorithm provides exactly that. For positive integers, replacing (a, b) with (b, a mod b) preserves their GCD. Repeatedly applying this transformation makes the second value smaller until it becomes zero. At that point, the first value is the GCD. The number of iterations is logarithmic in the smaller input.

There is also a small implementation improvement in the LCM formula. Instead of computing a * b // gcd(a, b), we can compute a // gcd(a, b) * b. Mathematically these are identical, but dividing first keeps intermediate values smaller. Python integers do not overflow, but this is still the conventional form and transfers directly to fixed-width languages such as C++.

The brute-force method works because it explicitly searches for the first common multiple, but fails because that search space can be enormous. The observation that the LCM is determined by the GCD lets us replace the entire search with the Euclidean algorithm.

Approach Time Complexity Space Complexity Verdict
Brute Force O(LCM(a, b)) O(1) Too slow
Optimal O(log min(a, b)) O(1) Accepted

Algorithm Walkthrough

  1. Read the two positive integers a and b. Their order does not matter because both GCD and LCM are symmetric.
  2. Compute g = gcd(a, b) using the Euclidean algorithm. While b is nonzero, replace (a, b) with (b, a % b). When b reaches zero, the remaining value of a is the GCD.

The reason this transformation works is that every common divisor of a and b also divides a % b, and every common divisor of b and a % b also divides a. Thus the set of common divisors does not change. 3. Restore the original values conceptually and calculate a // g * b. Dividing one input by the GCD removes exactly the factors already shared by the two numbers, so multiplying by the other input produces the smallest number containing all required factors. 4. Print the resulting LCM. Since both inputs are positive, the result is also positive.

Why it works

Let g = gcd(a, b). We can write a = g * x and b = g * y, where x and y have no common factor. Any common multiple of a and b must contain the shared factor g, while the remaining factors required by the two numbers are represented by x and y. Since x and y are coprime, the smallest number containing both is g * x * y, which is equal to a * b / g. The Euclidean algorithm computes exactly this g, so the final formula always produces the LCM.

Python Solution

import sys
input = sys.stdin.readline

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a

def solve():
    a, b = map(int, input().split())

    g = gcd(a, b)
    lcm = (a // g) * b

    print(lcm)

if __name__ == "__main__":
    solve()

The gcd function is the iterative form of the Euclidean algorithm. Each iteration replaces the pair with (b, a % b), and the remainder is strictly smaller than b, so the loop must terminate.

After the loop, a contains the GCD. The original input values are still available because the function parameters are local to gcd, so the LCM can be computed immediately afterward.

The expression (a // g) * b is preferable to a * b // g. Both give the same mathematical result, but dividing before multiplying avoids unnecessarily large intermediate values. Python has arbitrary-precision integers, so there is no integer overflow concern in this implementation.

There are no off-by-one conditions in the Euclidean algorithm. The loop continues exactly while the second value is nonzero. When it becomes zero, the first value is the GCD by definition of the algorithm.

Worked Examples

Sample 1

The input is:

2 4

The Euclidean algorithm and final calculation proceed as follows.

Step a b Operation
Start 2 4 Begin GCD
1 4 2 (2, 4) becomes (4, 2)
2 2 0 (4, 2) becomes (2, 0)
Finish 2 0 GCD = 2

Now g = 2, so the LCM is (2 // 2) * 4 = 4.

The GCD removes the factor already shared by both inputs, leaving exactly the factors needed for the smallest common multiple.

Sample 2

The input is:

1101 1816

The Euclidean algorithm gives:

Step a b Remainder
Start 1101 1816 1101
1 1816 1101 715
2 1101 715 386
3 715 386 329
4 386 329 57
5 329 57 44
6 57 44 13
7 44 13 5
8 13 5 3
9 5 3 2
10 3 2 1
11 2 1 0

The GCD is 1, so the two numbers are coprime. The LCM is consequently their product:

1101 * 1816 = 1999416.

The trace demonstrates that the Euclidean algorithm works regardless of which input is initially larger. The first iteration simply swaps the larger value into the first position as part of the normal (b, a % b) transformation.

Complexity Analysis

Measure Complexity Explanation
Time O(log min(a, b)) The Euclidean algorithm decreases the pair rapidly, requiring logarithmically many iterations.
Space O(1) Only a constant number of integer variables are stored.

The algorithm performs only a logarithmic number of modulo operations and does not depend on the magnitude of the LCM itself through iteration. This is the essential difference from brute-force enumeration, which could require up to Θ(a * b) checks for coprime inputs. The memory usage remains constant.

Test Cases

The following tests include both official samples and cases covering equal values, divisibility, coprime values, and large inputs.

import sys
import io

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a

def solve():
    a, b = map(int, input().split())
    g = gcd(a, b)
    print((a // g) * b)

def run(inp: str) -> str:
    global input
    old_stdin = sys.stdin
    old_input = input

    try:
        sys.stdin = io.StringIO(inp)
        input = sys.stdin.readline

        old_stdout = sys.stdout
        sys.stdout = io.StringIO()

        try:
            solve()
            return sys.stdout.getvalue()
        finally:
            sys.stdout = old_stdout
    finally:
        sys.stdin = old_stdin
        input = old_input

# Provided samples
assert run("2 4\n") == "4\n", "sample 1"
assert run("1101 1816\n") == "1999416\n", "sample 2"

# Equal values
assert run("7 7\n") == "7\n", "equal values"

# Coprime values
assert run("3 5\n") == "15\n", "coprime values"

# Large values, useful for catching brute-force solutions
assert run("999999937 1000000000\n") == "999999937000000000\n", "large coprime values"

# Divisibility and boundary behavior
assert run("1 1000000000\n") == "1000000000\n", "one divides the other"
Test input Expected output What it validates
2 4 4 Provided sample and divisibility
1101 1816 1999416 Provided sample and coprime inputs
7 7 7 Equal inputs
3 5 15 GCD equal to 1
999999937 1000000000 999999937000000000 Large values and avoidance of brute force
1 1000000000 1000000000 Boundary case where one input divides the other

Edge Cases

For equal inputs, consider:

7 7

The first Euclidean step changes (7, 7) to (7, 0), so the GCD is 7. The final calculation is (7 // 7) * 7 = 7. The output is:

7

A search-based implementation might perform unnecessary work, but the GCD-based method finishes immediately.

For the case where one number divides the other, consider:

2 4

The GCD is 2, and the LCM is (2 // 2) * 4 = 4. The output is:

4

The algorithm does not need to treat divisibility as a special case. It falls naturally out of the GCD formula.

For coprime numbers, consider:

3 5

The Euclidean algorithm produces a GCD of 1. The LCM is therefore (3 // 1) * 5 = 15, giving:

15

This checks the case where no common factor can be removed from the product.

For a large pair of coprime numbers, consider:

999999937 1000000000

Their GCD is 1, so the result is:

999999937000000000

A brute-force search would have to examine an enormous number of candidates before reaching this common multiple. The Euclidean algorithm instead finishes after only logarithmically many modulo operations, which is the central reason the optimal solution scales well.