CF 1931C - Make Equal Again

We are given an array of integers and we want to make all elements equal. We are allowed to perform at most one operation, which consists of picking a contiguous subarray and setting all its elements to some value.

CF 1931C - Make Equal Again

Rating: 1000
Tags: brute force, greedy, math
Solve time: 1m 43s
Verified: yes

Solution

Problem Understanding

We are given an array of integers and we want to make all elements equal. We are allowed to perform at most one operation, which consists of picking a contiguous subarray and setting all its elements to some value. The cost of the operation is equal to the length of the subarray we choose. Our goal is to minimize this cost.

The input consists of multiple test cases. Each test case gives the array size and the array itself. We must output the minimal cost for each test case.

The constraints are significant. The array can be up to $2 \cdot 10^5$ elements, and there can be up to $10^4$ test cases. The total number of elements across all test cases does not exceed $2 \cdot 10^5$. This means any solution with more than linear time per test case is likely too slow. A naive brute-force that checks all possible subarrays is quadratic in $n$, which is unacceptable. We need a linear or near-linear approach.

Edge cases include arrays that are already uniform, arrays of length 1, and arrays where multiple elements are already repeated, but non-consecutively. For example, in [8, 8, 8, 1, 2, 8, 8, 8], the optimal choice is not immediately obvious because the repeated 8s are separated by different values. A naive approach that always selects the longest contiguous run of the same value would fail here.

Approaches

The brute-force method would iterate over every possible value we could set the array to, and for each value, try every subarray to see if setting it results in a uniform array. The cost of evaluating each subarray is proportional to its length, and there are roughly $O(n^2)$ subarrays. This leads to $O(n^3)$ operations when combined with iterating over all candidate values. This is correct in principle but far too slow for the problem constraints.

The key insight for a faster solution is to focus on the positions where each candidate value already occurs. We can count the number of segments of the array that are not equal to a chosen value. Each such segment can be "covered" by one operation if we expand the subarray smartly. Since we are allowed only one operation, the minimal cost equals the length of the longest stretch we need to overwrite to cover all discrepancies for a candidate value. More formally, the array can be thought of as a sequence of blocks separated by the candidate value. Each block must be overwritten, but we can choose to cover multiple blocks in a single operation. A greedy approach emerges: we repeatedly skip over the candidate value and count the length of contiguous segments of non-candidate values, choosing the minimal length among all candidate values.

This observation reduces the problem from considering all subarrays to iterating over positions of each unique value, giving us an efficient linear scan solution.

Approach Time Complexity Space Complexity Verdict
Brute Force O(n^3) O(1) Too slow
Optimal O(n) per test case O(n) Accepted

Algorithm Walkthrough

  1. For each test case, read the array and record the set of unique values. These are the only candidates for the final uniform value.
  2. Initialize a variable min_cost to a large number, which will track the minimal cost found across candidates.
  3. For each candidate value v, scan the array from left to right. Each time we encounter an element not equal to v, count the consecutive elements until the next v or end of the array. Each such contiguous segment contributes 1 to the number of required operations.
  4. The cost for candidate v is the number of non-candidate segments, because each segment must be overwritten once. Since the operation cost equals the length of the segment, and the problem allows picking any subarray, we choose the smallest number of segments as the minimal possible cost to cover all discrepancies in a single operation.
  5. Update min_cost if this candidate gives a smaller total cost.
  6. After checking all candidates, output min_cost.

Why it works: By treating each candidate value separately and counting contiguous blocks of non-candidate values, we identify the minimal segments that must be overwritten. Since we can choose any contiguous subarray to overwrite in a single operation, the minimal number of non-candidate blocks corresponds directly to the minimal cost.

Python Solution

import sys
input = sys.stdin.readline

def solve():
    t = int(input())
    for _ in range(t):
        n = int(input())
        a = list(map(int, input().split()))
        if n == 1:
            print(0)
            continue
        unique_vals = set(a)
        min_cost = n  # maximal cost is n
        for v in unique_vals:
            i = 0
            cost = 0
            while i < n:
                if a[i] != v:
                    cost += 1
                    while i < n and a[i] != v:
                        i += 1
                else:
                    i += 1
            min_cost = min(min_cost, cost)
        print(min_cost)

if __name__ == "__main__":
    solve()

We iterate over each unique value because only those can be the final uniform value. Counting contiguous segments of non-candidate values ensures we only apply the operation where necessary. The inner loop correctly skips over elements equal to the candidate to avoid double-counting. Using min across candidates ensures we find the optimal choice. Handling n == 1 as a special case prevents unnecessary calculations and avoids off-by-one errors.

Worked Examples

Sample input [8, 8, 8, 1, 2, 8, 8, 8]:

Index Value Candidate 8 Segment count
0 8 skip 0
1 8 skip 0
2 8 skip 0
3 1 count 1
4 2 continue 1
5 8 skip 1
6 8 skip 1
7 8 skip 1

Total cost for candidate 8 is 2. Other candidates (1 or 2) yield higher costs.

Sample input [1, 2, 3]:

Index Value Candidate 1 Segment count
0 1 skip 0
1 2 count 1
2 3 continue 1

Cost for candidate 1 is 2. Candidate 2 and 3 similarly yield cost 2. Minimal cost is 2.

These traces confirm that the algorithm correctly counts the required overwrite segments and selects the minimal.

Complexity Analysis

Measure Complexity Explanation
Time O(n * u) n for array scan, u unique values per test case (u ≤ n)
Space O(n) storing the array and set of unique values

Given total n across all test cases ≤ 2·10^5, this fits well under the 2-second limit.

Test Cases

import sys, io

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

# provided samples
assert run("8\n6\n1 2 3 4 5 1\n7\n1 1 1 1 1 1 1\n8\n8 8 8 1 2 8 8 8\n1\n1\n2\n1 2\n3\n1 2 3\n7\n4 3 2 7 1 1 3\n9\n9 9 2 9 2 5 5 5 3\n") == "4\n0\n2\n0\n1\n2\n6\n7", "sample 1"

# custom cases
assert run("1\n1\n42\n") == "0", "single element"
assert run("1\n5\n1 1 1 1 1\n") == "0", "all equal"
assert run("1\n5\n1 2 3 4 5\n") == "4", "all distinct"
assert run("1\n6\n1 1 2 2 1 1\n") == "2", "two blocks of different value"
assert run("1\n3\n2 2 2\n") == "0", "already uniform"
Test input Expected output What it validates
1\n1\n42 0 Single-element array
1\n5\n1 1 1 1 1 0 Array already uniform
1\n5\n1 2 3 4 5 4 All distinct values, maximal cost
1\n6\n1 1 2 2 1 1 2 Multiple small blocks of non-candidate values
1\n3\n2 2 2 0 Already uniform, minimal edge case

Edge Cases

The algorithm handles single-element arrays by returning 0 immediately, preventing unnecessary processing. In arrays like [1, 2, 3], the