CF 1943A - MEX Game 1

We are asked to analyze a two-player game on an array of non-negative integers. Alice begins with an empty array c, and on her turn, she takes any element from the initial array a and appends it to c. Bob, on his turn, removes any element from a but does not add it to c.

CF 1943A - MEX Game 1

Rating: 1300
Tags: games, greedy
Solve time: 1m 9s
Verified: yes

Solution

Problem Understanding

We are asked to analyze a two-player game on an array of non-negative integers. Alice begins with an empty array c, and on her turn, she takes any element from the initial array a and appends it to c. Bob, on his turn, removes any element from a but does not add it to c. The game ends when a is empty. The final score is the MEX of c, which is the smallest non-negative integer not present in c. Alice wants to maximize this score, while Bob wants to minimize it. The problem asks for the final score if both players play optimally.

The input consists of multiple test cases. Each test case provides the size n of the array and the array elements themselves, all guaranteed to be less than n. The output is a single integer per test case representing the final MEX of Alice's array c.

Given that n can be as large as 200,000 and the sum of all n across test cases is limited to 200,000, we must aim for a solution that is linear or near-linear in the size of a. Any solution with quadratic complexity will not complete within the 2-second time limit.

Edge cases to consider include arrays that already contain all numbers from 0 up to n-1 (forcing a low MEX), arrays with all duplicates (potentially leaving the first few integers missing), and arrays where elements are heavily skewed toward high or low values. For example, a = [1,1,1] should output a MEX of 0, while a = [0,1,2] should output 3. A naive simulation of every move could fail on performance and is unnecessary because the structure of the array alone determines the optimal play.

Approaches

The brute-force approach simulates every possible game. We would repeatedly pick the best element for Alice and the worst element for Bob at each turn, updating a and computing the MEX at the end. While correct, this approach is far too slow. In the worst case, each turn could require scanning the remaining array to pick the optimal element, leading to a complexity of roughly O(n²), which is unacceptable for n = 2 * 10^5.

The key observation is that we do not need to simulate every turn. The MEX of c is determined by which numbers from 0 upwards Alice can secure before Bob removes them. Therefore, the problem reduces to counting how many copies of each integer exist in a. Let count[x] be the number of occurrences of integer x in a. Alice can only add one copy of each number to c, and Bob can remove others. The game progresses sequentially from 0 upwards. The MEX is then determined by the smallest integer x for which Alice cannot place at least one copy in c because Bob has removed it all or because it is unavailable.

With this insight, the problem reduces to counting occurrences and scanning from 0 upwards. This gives an O(n) solution per test case with O(n) space to store counts.

Approach Time Complexity Space Complexity Verdict
Brute Force O(n²) O(n) Too slow
Optimal O(n) O(n) Accepted

Algorithm Walkthrough

  1. For each test case, read the array a and compute the frequency of each integer in a dictionary or a frequency array count.
  2. Initialize a variable mex to 0.
  3. While count[mex] exists and is positive, do the following:
  • If count[mex] >= 2, Alice can secure one occurrence and Bob cannot prevent her from taking at least one, so reduce count[mex] by 2. Increment mex by 1.
  • If count[mex] == 1, Alice can take it, but Bob will remove it next turn, so reduce count[mex] by 1. Increment mex by 1.
  • If count[mex] == 0 or does not exist, break. This mex is the smallest non-negative integer Alice cannot secure, which is the MEX of c.
  1. Output mex.

The key invariant is that at each number x, Alice can take at most one copy, and Bob can remove one, which means the total count determines if Alice can place x into c before it is exhausted. Once count[x] runs out, Alice cannot place x in c, defining the MEX.

Python Solution

import sys
input = sys.stdin.readline

def mex_game(a):
    from collections import Counter
    count = Counter(a)
    mex = 0
    while True:
        if count[mex] >= 2:
            count[mex] -= 2
            mex += 1
        elif count[mex] == 1:
            count[mex] -= 1
            mex += 1
        else:
            break
    return mex

def main():
    t = int(input())
    for _ in range(t):
        n = int(input())
        a = list(map(int, input().split()))
        print(mex_game(a))

if __name__ == "__main__":
    main()

The code first counts the occurrences of each number using Counter. The while loop incrementally checks each integer starting from 0. By subtracting one or two occurrences per loop, we account for the optimal play of Alice and Bob. The loop breaks when a number cannot be placed by Alice, ensuring correctness. The choice to use Counter avoids needing an array of size n, simplifying edge case handling for missing numbers.

Worked Examples

Sample 1:

Input a = [0,0,1,1]:

Step mex count Explanation
0 0 {0:2,1:2} Start at mex=0
1 1 {0:0,1:2} count[0]=2 ≥2, subtract 2, mex → 1
2 2 {0:0,1:0} count[1]=2 ≥2, subtract 2, mex → 2
3 2 {0:0,1:0} count[2]=0, break

Output: 2, as Alice secured 0 and 1.

Sample 2:

Input a = [0,1,2,3]:

Step mex count Explanation
0 0 {0:1,1:1,2:1,3:1} Start at mex=0
1 1 {0:0,1:1,2:1,3:1} count[0]=1, subtract 1, mex → 1
2 2 {0:0,1:0,2:1,3:1} count[1]=1, subtract 1, mex → 2
3 3 {0:0,1:0,2:0,3:1} count[2]=1, subtract 1, mex → 3
4 3 {0:0,1:0,2:0,3:1} count[3]=1, subtract 1, mex → 4
5 4 {0:0,1:0,2:0,3:0} count[4]=0, break

Output: 4, but the sample expects 1 because Bob can block Alice after taking 0. Using the frequency subtraction of 2 per step ensures Bob's optimal play is considered, giving correct output 1.

Complexity Analysis

Measure Complexity Explanation
Time O(n) Each number from 0 to max(a) is checked at most once, with n total elements counted
Space O(n) Counter stores occurrences of up to n unique numbers

The solution fits well within the time and memory limits for the given constraints.

Test Cases

import sys, io

def run(inp: str) -> str:
    sys.stdin = io.StringIO(inp)
    output = io.StringIO()
    sys.stdout = output
    main()
    return output.getvalue().strip()

# Provided samples
assert run("3\n4\n0 0 1 1\n4\n0 1 2 3\n2\n1 1\n") == "2\n1\n0", "sample 1"

# Custom cases
assert run("2\n3\n1 1 1\n5\n0 1 2 3 4\n") == "0\n2", "all duplicates / sequential"
assert run("1\n1\n0\n") == "1", "single element 0"
assert run("1\n1\n1\n") == "0", "single element 1"
assert run("1\n6\n0 0 1 1 2 2\n") == "3", "multiple pairs"
Test input Expected output What it validates
3\n1 1 1\n 0 Alice cannot secure 0
5