Many Div. 2 A–C and some Div. 1 problems ask for “the minimum number of operations”, and the answer is always in a set like or . The method:

  1. Find a universal strategy that always works in operations (often “apply the operation to the whole array” a couple of times).
  2. Characterize exactly when operations suffice. Each characterization is usually a simple scan.
  3. Prove the lower bounds: when the cheaper checks fail, fewer operations are impossible.

The brute-force table from Observation Toolkit shows the bounded answer at once.

Example 1: sorting by rearranging proper subarrays

Operation: choose a subarray that is not the whole array and rearrange it arbitrarily. Minimum operations to sort a permutation of ():

int permutationSort(const vector<int>& p) {    // rearrange any proper subarray; values 1..n
    int n = sz(p);
    if (is_sorted(all(p))) return 0;
    if (p[0] == 1 || p[n - 1] == n) return 1;
    if (p[0] == n && p[n - 1] == 1) return 3;
    return 2;
}

Example 2: make everything zero with mex

Operation: choose and set every () to . Minimum operations to make the array all zeros:

int mexToZero(const vector<int>& a) {          // set a[l..r] := mex(a[l..r]); make everything 0
    int l = 0, r = sz(a) - 1;
    while (l <= r && a[l] == 0) l++;
    while (r >= l && a[r] == 0) r--;
    if (l > r) return 0;
    for (int i = l; i <= r; i++) if (a[i] == 0) return 2;
    return 1;
}

Where small answers hide

SignalUniversal strategy
”operation on any subarray / substring”apply to the whole thing, or to a prefix plus a suffix
”minimum number of operations” with a casean invariant decides ; otherwise the answer is or
operations with mex, gcd, or, andapplying to everything collapses the array to a constant
”make the array good” where good is a local conditionfix one end, then the other

Related: Proof Techniques.