Codeforces Round 1133 (Div. 1, Div. 2) is coming soon! Register here. ×
O(n^2) approach to USACO 248( Bypassing the editorial of O(n^3) Range DP)
Difference between en2 and en3, changed 16 character(s)
**Problem reference:-** [USACO 248 (2016 US Open, Gold)](http://www.usaco.org/index.php?page=viewproblem2&cpid=646)↵
↵
I recently solved the USACO. The standard expected solution is an O(n^3) range dp. I tried a greedy divide and conquer approach with O(nlogn) avg. and O(n^2) worst case complexity, which to my surprise worked.↵
↵
I wanted to share the logic here to see if this is a known trick for similar combination games.↵
↵
The problem asks us to find the maximum possible number we can generate by adjacent equal numbers.↵
↵
**The O(N^2) Observation**↵
Instead of looking at all possible subarray splits, we can just look at the global minimum element in the current array. Let this be **mini**. Because it is the absolute minimum, it is impossible to ever create a new mini. You can only combine existing ones. ↵
-> Even length Clumps of adj **mini** Collapse↵
-> Odd length clumps are permanent partitions↵
↵
**The Algorithm**↵
Find mini in the current array.↵
Traverse the array and group adjacent minis into clumps.↵
For purely even clumps, convert them directly into mini+1.↵
For odd clumps, use them as split points to divide the array into smaller chunks, branching to try collapsing the left-side of the clump vs the right-side of the clump.↵
Recursively call the function on these new, smaller partitioned arrays.↵
↵
↵
~~~~~↵
#include <bits/stdc++.h>↵
using namespace std;↵
↵
#define ll long long↵
↵
ll func(const vector<ll>& arr) {↵
    int n=arr.size();↵
    if (n==0)return -1;↵
    if (n==1)return arr[0];↵
↵
//Find the global minimum element↵
    ll mini=1e18;↵
    for(ll x:arr) {↵
        mini=min(mini,x);↵
    }↵
↵
//Identify partition points (boundaries of odd clumps)↵
    vector<ll>part_start,part_end;↵
    ll pre=-1;↵
    ll cnt=0;↵
    ↵
//Find starting boundaries for partitions↵
    part_start.push_back(-1);↵
    for (int j=0; j<n; j++) {↵
        if (arr[j]!=pre&&arr[j]==mini) {↵
            if (cnt%2==0&&cnt!=0)part_start.pop_back();↵
            part_start.push_back(j);↵
            cnt=0;↵
        }↵
        if(arr[j]==mini)cnt++;↵
        pre=arr[j];↵
    }↵
    if(cnt%2==0&&cnt!=0)part_start.pop_back();↵
↵
    pre=-1;↵
    cnt=0;↵
    ↵
//Find ending boundaries for partitions↵
    for(int j=n-1; j>=0; j--) {↵
        if(arr[j]!=pre&&arr[j]==mini) {↵
            if (cnt%2==0&&cnt!=0)part_end.pop_back();↵
            part_end.push_back(j);↵
            cnt=0;↵
        }↵
        if(arr[j]==mini)cnt++;↵
        pre=arr[j];↵
    }↵
    if (cnt%2==0&&cnt!=0)part_end.pop_back();↵
↵
    reverse(part_end.begin(),part_end.end());↵
    part_end.push_back(n);↵
↵
    ll ans=0;↵
↵
    ↵
↵
//Process elements inside each valid partition↵
    for(int p=0; p<part_start.size(); p++) {↵
        vector<ll>temp;↵
        ↵
        int ind1=part_start[p]+1;↵
        int ind2=part_end[p]-1;↵
        int cnt1=0, cnt2=0;↵
        ↵
        //Count overlapping minimums at the boundaries↵
        while(ind1<=ind2&&arr[ind1]==mini) {↵
            cnt1++;↵
            ind1++;↵
        }↵
        while(ind2>=ind1&&arr[ind2]==mini) {↵
            cnt2++;↵
            ind2--;↵
        }↵
↵
        //Collapse even pairs of 'mini' on the left boundary↵
        for (int i=0; i<cnt1/2; i++) {↵
            temp.push_back(mini+1);↵
        }↵
        ↵
        //Process the middle part: collapse isolated pairs of 'mini'↵
        for (int i=ind1; i<=ind2; i++) {↵
            if(arr[i]==mini){↵
                temp.push_back(mini+1);↵
                i++; // Skip the paired element↵
            }↵
            else{↵
                temp.push_back(arr[i]);↵
            }↵
        }↵
        ↵
        //Collapse even pairs of 'mini' on the right boundary↵
        for (int i=0; i<cnt2/2; i++) {↵
            temp.push_back(mini+1);↵
        }↵
↵
        //Recursively branch on this partitioned state↵
        ans=max(ans,func(temp));↵
    }↵
↵
    return ans;↵
}↵
↵
int main() {↵
    freopen("248.in","r",stdin);↵
    freopen("248.out","w",stdout);↵
    ll n;↵
    cin >> n;↵
    vector<ll>arr(n);↵
↵
    for (auto &x:arr)cin>>x;↵
↵
    cout<<func(arr)<<"\n";↵
    ↵
}
↵
~~~~~

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en4 English BASANT_KUMAR 2026-07-13 19:38:22 7
en3 English BASANT_KUMAR 2026-07-13 18:08:41 16
en2 English BASANT_KUMAR 2026-07-13 18:07:27 11
en1 English BASANT_KUMAR 2026-07-13 18:05:02 4179 Initial revision (published)