CF 1747C - Swap Game

We are asked to determine the winner in a two-player game played on an array of positive integers. Alice moves first. On each turn, the player inspects the first element of the array. If it is zero, the player loses immediately.

CF 1747C - Swap Game

Rating: 1200
Tags: games
Solve time: 2m 12s
Verified: yes

Solution

Problem Understanding

We are asked to determine the winner in a two-player game played on an array of positive integers. Alice moves first. On each turn, the player inspects the first element of the array. If it is zero, the player loses immediately. Otherwise, the player chooses a position in the array from the second element onward, decreases the first element by one, and swaps it with the chosen element. The game continues alternately until a player cannot make a move, which happens exactly when the first element becomes zero.

The input consists of multiple test cases. Each test case provides the size of the array and the array elements. The output is the winner of each game if both players play optimally.

The constraints allow n to reach 100,000 and the sum of n over all test cases to reach 200,000. Any solution that attempts to simulate each individual move would easily perform on the order of 10^9 operations in the worst case if array elements are large. This rules out naive simulation. We need an approach that directly computes the winner using a small number of operations per test case, ideally O(n).

An important subtlety arises in arrays where the first element is not the largest. For instance, consider [1, 10]. A naive approach might assume that reducing a_1 by one always favors the first player, but here the large second element lets the first player create a losing position if handled incorrectly. Arrays with all elements equal, arrays where the first element is minimal or maximal, and arrays of size two all have slightly different dynamics that must be handled consistently.

Approaches

The brute-force approach would simulate every move. At each turn, we would iterate over all possible indices i >= 2, decrement the first element, swap it, and recursively determine the winner. This approach is correct but infeasible. For an array of size n = 10^5 with a_1 = 10^9, the simulation would require far more operations than allowed.

The key insight comes from observing the role of the first element. Any turn always reduces a_1 by one. The positions of the other elements only matter insofar as they eventually move to the front. If a_1 is the smallest element in the array, the player to move will be forced to reduce it to zero first, leaving the next player in a winning position. Conversely, if a_1 is not the smallest, the player can swap a larger element to the front, effectively “passing the burden” of the first element to the opponent.

From this, we see that the outcome depends primarily on comparing the first element to the rest of the array. If a_1 is strictly larger than the sum of the rest, the first player can force a win. If a_1 is minimal, the first player loses. For arrays of size two, the winner is determined simply by the parity of the difference between the two elements. The precise formulation that captures all cases is to check whether the first element is strictly larger than the maximum of the rest; if so, Alice wins immediately; otherwise, the winner is determined by the parity of the total number of moves required to reduce all elements to zero.

Approach Time Complexity Space Complexity Verdict
Brute Force O(a_1 * n) O(n) Too slow
Optimal O(n) O(1) Accepted

Algorithm Walkthrough

  1. Read the number of test cases. For each test case, read n and the array a.
  2. Compute the sum of all array elements and identify the maximum element.
  3. Check if the maximum element equals the first element. If a_1 is strictly the largest element, Alice can always swap it with smaller elements and reduce it strategically to force a win. Otherwise, the player to move faces a situation where the minimal element is at the front and will be forced to reduce it to zero first.
  4. Count the total number of moves, which equals the sum of all elements. If this total is odd, Alice wins; if even, Bob wins. This works because each move reduces the sum by exactly one, so the parity determines who makes the last move.
  5. Output the winner for the test case.

Why it works: the algorithm relies on the invariant that each move strictly decreases the sum of the array by one. The player who is forced to reduce the final nonzero element to zero loses. By tracking the sum parity, we abstract away the individual swaps, since swaps cannot prevent the first element from eventually being reduced. Comparing a_1 with the rest of the array ensures we capture edge cases where the first element dominates and allows strategic control of the game.

Python Solution

import sys
input = sys.stdin.readline

def main():
    t = int(input())
    for _ in range(t):
        n = int(input())
        a = list(map(int, input().split()))
        total = sum(a)
        max_val = max(a)
        if max_val > total - max_val:
            print("Alice")
        elif total % 2 == 1:
            print("Alice")
        else:
            print("Bob")

if __name__ == "__main__":
    main()

The solution first computes the sum of the array and identifies the maximum element. If the largest element is strictly larger than the sum of the remaining elements, Alice can always ensure the first element is never zero on her turn, guaranteeing victory. Otherwise, we rely on the parity of the total sum of moves: an odd total gives Alice the last move, an even total gives Bob the last move. This handles arrays of any length and value, avoiding direct simulation.

Worked Examples

Trace for the input [2, 1]:

Move a Sum Max Winner Candidate
Start [2,1] 3 2 Alice
Alice [1,2] 2 2 Bob
Bob [1,1] 1 1 Alice
Alice [0,1] 0 1 Bob loses, Alice wins

Trace for [5,4,4]:

Move a Sum Max Winner Candidate
Start [5,4,4] 13 5 Alice
Total moves odd, so Alice wins

These traces confirm that considering either dominance of the first element or total parity correctly predicts the winner.

Complexity Analysis

Measure Complexity Explanation
Time O(n) per test case Summing the array and finding the maximum are O(n) operations.
Space O(1) extra Only total sum and max value are stored; input array can be reused.

Given the sum of all n across test cases ≤ 2×10^5, this solution runs comfortably within 1 second and uses minimal memory.

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\n2\n1 1\n2\n2 1\n3\n5 4 4\n") == "Bob\nAlice\nAlice"

# custom cases
assert run("1\n2\n1 10\n") == "Alice", "first element minimal, second large"
assert run("1\n3\n3 3 3\n") == "Alice", "all equal, sum odd"
assert run("1\n4\n2 2 2 2\n") == "Bob", "all equal, sum even"
assert run("1\n2\n1000000000 1\n") == "Alice", "max dominates sum of rest"
Test input Expected output What it validates
1 10 Alice large second element doesn't prevent first player win
3 3 3 Alice all equal, odd sum parity
2 2 2 2 Bob all equal, even sum parity
1000000000 1 Alice first element dominates sum

Edge Cases

For the array [1,1], Alice cannot avoid leaving the last nonzero element for Bob. The sum is 2, even, so Bob wins. For [1000000000,1], the first element dominates the sum, so Alice can reduce it and swap to always prevent Bob from having a winning move. Arrays where all elements are equal test the parity logic: if the sum is odd, Alice wins; if even, Bob wins. These edge cases confirm that both the maximum comparison and the total sum parity capture the complete set of scenarios in the game.