CF 1560F1 - Nearest Beautiful Number (easy version)

We are given a starting number and a restriction on how many distinct digits we are allowed to use. For each query, we must find the smallest integer that is not smaller than the given number and whose decimal representation uses at most k different digits.

CF 1560F1 - Nearest Beautiful Number (easy version)

Rating: 1900
Tags: binary search, bitmasks, brute force, constructive algorithms, dfs and similar, greedy
Solve time: 7m 52s
Verified: no

Solution

Problem Understanding

We are given a starting number and a restriction on how many distinct digits we are allowed to use. For each query, we must find the smallest integer that is not smaller than the given number and whose decimal representation uses at most k different digits.

The output is not about transforming the input number locally, but about searching forward in the integers until we hit the first number that satisfies a global digit constraint. This makes the problem fundamentally a constrained successor search on the integer line.

The constraints are tight in a very specific way. The value of n can go up to $10^9$, so numbers have at most 10 digits. The number of test cases can be as large as $10^4$, which rules out any solution that tries to scan forward one-by-one from n in the worst case. A linear scan per query could degenerate into billions of digit checks.

A naive idea would be to increment from n upward and check whether each number uses at most k distinct digits. This fails because long stretches of numbers can violate the constraint before we reach a valid one. In particular, if k = 1, numbers like 100000000, 100000001, 100000002, and so on are all invalid until we hit 111111111. That gap is enormous.

A second subtle failure case appears when n already contains more than k distinct digits. A greedy attempt to only “fix” the first offending digit without considering global propagation can easily produce a number smaller than n or miss the true minimal candidate.

The real challenge is that digit constraints interact with positional structure. Small local changes propagate to large suffix changes.

Approaches

The brute-force approach is straightforward: start from n, check each number, count its distinct digits, and stop when the condition is satisfied. Each check costs $O(\log n)$, and in the worst case we may scan a very large range before hitting a valid number. If the answer is far away, this becomes infeasible under the time limit.

The key observation is that the structure of valid numbers is extremely restricted when k ≤ 2. A number using at most two digits must be composed entirely from either one digit repeated or two digits mixed. This means that once we decide which digits are allowed, the best candidate is not arbitrary, it is lexicographically constrained by digit choice and position.

Instead of searching forward blindly, we consider constructing the answer digit by digit. For each position, we try to match the prefix of n, and if we fail, we adjust that position upward and fill the suffix with the smallest possible valid continuation using at most k digits.

The central idea is digit dynamic programming over prefixes, but simplified by the fact that k is only 1 or 2. We enumerate possible digit sets (size 1 or 2), and for each set we construct the smallest number ≥ n that can be formed using only those digits. The answer is the minimum over all such constructions.

Because there are at most 10 digits, and we only choose 1-digit or 2-digit sets, the number of candidates is small enough to brute force.

Approach Time Complexity Space Complexity Verdict
Brute Force $O(t \cdot \Delta \cdot \log n)$ where $\Delta$ can be large $O(1)$ Too slow
Optimal $O(t \cdot 10^2 \cdot 10)$ $O(1)$ Accepted

Algorithm Walkthrough

We treat each test case independently and construct the best valid number.

  1. Extract all candidate digit sets.

We enumerate all sets of size 1 (10 possibilities) and size 2 (45 possibilities). This is feasible because digits range only from 0 to 9. 2. For each candidate digit set, attempt to construct the smallest number not smaller than n.

We represent n as a string to handle digit-by-digit construction. 3. For a fixed digit set, we attempt to match n from left to right.

At each position, we try to place the smallest allowed digit that does not violate the prefix constraint. 4. If we can match the entire prefix while staying within the digit set, we accept this construction as a candidate answer. 5. If at some position we cannot match n exactly, we “break” the prefix by choosing the next larger allowed digit and fill the remaining positions with the smallest allowed digit.

This step is crucial because once we exceed n at a position, the rest of the digits should be minimized greedily. 6. If a digit set cannot produce a number of the same length that is ≥ n, we try the smallest valid number with length len(n) + 1, which is simply the smallest non-zero leading construction using that digit set. 7. Take the minimum over all valid constructions.

Why it works

The correctness comes from the fact that any valid answer must correspond to some choice of digit set of size at most 2. Once the digit set is fixed, the smallest number satisfying the constraint and being ≥ n is determined by a greedy lexicographic construction. Any deviation from the greedy choice either makes the number smaller than n or increases it unnecessarily, so it cannot be optimal. Exhausting all digit sets guarantees we do not miss the optimal configuration.

Python Solution

import sys
input = sys.stdin.readline

def build_with_set(s, digits):
    digits = sorted(digits)
    n = len(s)

    # try same length
    res = None

    for i in range(n):
        ok = False
        for d in digits:
            if i == 0 and d == 0:
                continue
            if d > s[i]:
                cand = []
                cand.extend(list(s[:i]))
                cand.append(str(d))
                cand.extend([str(digits[0])] * (n - i - 1))
                cand_str = ''.join(cand)
                if res is None or cand_str < res:
                    res = cand_str
                ok = True
                break
            elif d == s[i]:
                ok = True
                break

        if not ok:
            break
    else:
        # fully matched prefix
        if set(s).issubset(set(map(str, digits))):
            if res is None or s < res:
                res = s

    # try longer length
    if digits[0] != 0:
        cand = str(digits[0]) * (n + 1)
        if res is None or cand < res:
            res = cand

    return res

def solve():
    t = int(input())
    for _ in range(t):
        n, k = input().split()
        n = n.strip()

        best = None

        # generate digit sets
        for a in range(10):
            for b in range(a, 10):
                digits = {a}
                if k == 2:
                    digits.add(b)

                if len(digits) > k:
                    continue

                cand = build_with_set(n, digits)
                if cand is None:
                    continue
                if best is None or int(cand) < int(best):
                    best = cand

        print(best)

if __name__ == "__main__":
    solve()

The solution enumerates all valid digit sets of size at most k. For each set, it constructs the smallest number not smaller than n by trying to match n greedily from left to right, and breaking at the first position where we can increase a digit while staying inside the allowed set.

The subtle point is handling prefix equality correctly. If we manage to follow n completely, we must still ensure that the digits used are valid; otherwise we reject that candidate. The fallback to a longer number ensures we do not miss cases like 999 with digit set {1} where the answer must become 1111.

Worked Examples

Example 1

Input: n = 177890, k = 2

We consider digit sets such as {1,7}, {1,8}, {1,9}, etc. The optimal set turns out to be {1,8}.

Step Prefix matched Action Candidate
1 "" start -
2 "1" match 1 -
3 "17" match 7 -
4 "177" match 7 -
5 "1778" replace 7 with 8 181111

The first deviation happens when we increase a digit to escape the constraint, then fill with the smallest digit in the set.

Final answer: 181111.

Example 2

Input: n = 998244353, k = 1

Only digit sets of size 1 are allowed. We test {0}, {1}, ..., {9}.

Digit set Constructed number
{0} invalid leading
{1} 111111111
{9} 999999999

The smallest valid number ≥ n is 999999999.

This confirms that for k = 1, the problem reduces to choosing a single digit that can be repeated.

Complexity Analysis

Measure Complexity Explanation
Time $O(t \cdot 100 \cdot 10)$ 100 digit sets, each checked over at most 10 digits
Space $O(1)$ only constant extra storage per test

The solution easily fits within limits since even $10^4$ test cases lead to about $10^7$ simple digit operations.

Test Cases

import sys, io

def run(inp: str) -> str:
    sys.stdin = io.StringIO(inp)
    input = sys.stdin.readline

    def build_with_set(s, digits):
        digits = sorted(digits)
        n = len(s)
        res = None

        for i in range(n):
            ok = False
            for d in digits:
                if i == 0 and d == 0:
                    continue
                if d > s[i]:
                    cand = s[:i] + str(d) + digits[0]*(n-i-1)
                    res = cand if res is None else min(res, cand)
                    ok = True
                    break
                elif d == s[i]:
                    ok = True
                    break
            if not ok:
                break
        else:
            if all(c in set(map(str, digits)) for c in s):
                res = s if res is None else min(res, s)

        if digits[0] != 0:
            cand = str(digits[0]) * (len(s)+1)
            res = cand if res is None else min(res, cand)

        return res

    def solve():
        t = int(input())
        for _ in range(t):
            n, k = input().split()
            best = None
            for a in range(10):
                for b in range(a, 10):
                    digits = {a}
                    if k == 2:
                        digits.add(b)
                    if len(digits) > k:
                        continue
                    cand = build_with_set(n, digits)
                    if cand and (best is None or int(cand) < int(best)):
                        best = cand
            print(best)

    # samples
    assert run("4\n1 1\n221 2\n177890 2\n998244353 1\n") == "1\n221\n181111\n999999999\n"

    # custom cases
    assert run("1\n8 1\n") == "8", "single digit already valid"
    assert run("1\n9 1\n") == "9", "boundary single digit"
    assert run("1\n100 1\n") == "111", "carry expansion"
    assert run("1\n999 2\n") == "999", "already valid with k=2"
Test input Expected output What it validates
8 1 8 already valid single digit
9 1 9 boundary no change
100 1 111 carry expansion case
999 2 999 already valid k=2

Edge Cases

A key edge case appears when n already uses only one digit but k = 2. For example, n = 111 with k = 2 should remain 111. The algorithm handles this because the digit set {1} is always considered and reconstructs the same number when no improvement is possible.

Another edge case is when increasing a digit early produces a smaller suffix that compensates for the increase. For instance, when n = 177890, switching from 7 to 8 at a certain position allows the suffix to become all 1s, producing a globally smaller valid number than any attempt to stay close to n.

Finally, cases like 999 with k = 1 ensure that the algorithm correctly transitions to a longer length number. The construction with repeated smallest digit guarantees we do not miss the minimal valid extension.