Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
A nice array is exactly an array where every maximal equal-value run has length at least . So think in terms of grouping adjacent positions into constant blocks.
For a fixed block of positions, the best value to make them all equal to is a median. For length , the cost is . For length , the cost is .
You never need a block of length or more. Any such block can be split into blocks of length and , and letting the smaller blocks choose their own medians cannot make the cost worse.
So the whole problem becomes: partition the circular array into consecutive pieces of length or , minimizing the sum of their costs.
Cut the circle between and . Only one piece can cross that cut, and it can only be , , or . Try those cases, plus the case where nothing crosses, and run a simple linear DP each time.
A target array is nice iff there are no lonely equal-runs of length .
More concretely, if you compress equal adjacent values into circular runs, then every run must have length at least . That is the whole condition. No hidden graph nonsense, no weird global dependency. Just: every position must belong to some constant adjacent group of size at least .
Cost of One Block
Suppose we decide that positions will all become the same value . The cost is
This is minimized by choosing as a median of those values.
For this problem, we only need block sizes and :
because the median is the middle value.
Why Blocks Larger Than 3 Are Useless
This is the key trick.
Take any constant block of length . You can split it into smaller consecutive blocks of lengths and . This is always possible for every :
Now let each smaller block choose its own best value. That can only decrease or preserve the cost, because choosing values independently is more flexible than forcing the entire big block to share one value.
And every smaller block still has length at least , so every element still has an equal neighbor inside its own block. Nice condition satisfied. No drama.
Therefore, there is always an optimal answer where every block has length exactly or .
So the problem is now:
Partition the circular array into consecutive groups of size or , minimizing total median cost.
Linear DP
First ignore circularity. For a normal interval , define as the minimum cost to cover the first elements of this interval.
Base:
Transitions:
where
and
If the interval length is impossible to cover, like length , the DP just returns infinity.
Handling the Circle
Cut the circle between positions and .
In the optimal partition, either no block crosses this cut, or exactly one block crosses it. Since block sizes are only or , the crossing block has only three possible shapes:
So try four cases:
Take the minimum.
That is it. The circle tries to look scary, but once blocks are capped at size , it has only three ways to be annoying.
Correctness Proof
We prove the algorithm returns the minimum possible cost.
First, every nice array can be viewed as circular constant runs, each of length at least . This follows directly from the definition: if a run had length , that element would differ from both neighbors and would be alone.
Second, there exists an optimal nice array whose constant blocks all have length or . Consider any block of length at least . Split it into consecutive pieces of length and . Each new piece has length at least , so every element still has an equal neighbor inside its piece. Also, each new piece may choose its own median value, so the total cost cannot increase compared to forcing the original larger block to use one value. Repeating this removes all blocks of length at least .
Thus, the original problem is equivalent to partitioning the circular array into consecutive blocks of length or , with each block paid by its optimal median cost.
For a fixed linear interval, the DP is correct because the final block in any valid partition must have length or . Removing that final block leaves an optimal partition of the previous prefix; otherwise we could improve the full solution. The recurrence checks both possible final block sizes, so it finds the optimal linear cost.
For the circular case, after cutting between and , at most one block crosses the cut. Since every block has length or , that crossing block must be exactly one of , , or . If no block crosses, the whole circle is just one linear interval. The algorithm tries all four exhaustive cases and solves the remaining non-circular interval optimally with the DP.
Therefore, the minimum among those cases is exactly the optimal answer.
Complexity
Each linear DP is , and we run it only four times.
Total complexity per test case is:
with memory. Across all tests, this is easily fine for .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
const ll INF = (1LL << 62);
while (t--) {
int n;
cin >> n;
vector<ll> a(n);
for (ll &x : a) cin >> x;
auto cost2 = [&](int i, int j) -> ll {
return llabs(a[i] - a[j]);
};
auto cost3 = [&](int i, int j, int k) -> ll {
ll mn = min({a[i], a[j], a[k]});
ll mx = max({a[i], a[j], a[k]});
return mx - mn;
};
auto solveLinear = [&](int l, int r) -> ll {
if (l > r) return 0LL;
int m = r - l + 1;
vector<ll> dp(m + 1, INF);
dp[0] = 0;
for (int i = 1; i <= m; i++) {
if (i >= 2) {
dp[i] = min(dp[i], dp[i - 2] + cost2(l + i - 2, l + i - 1));
}
if (i >= 3) {
dp[i] = min(dp[i], dp[i - 3] + cost3(l + i - 3, l + i - 2, l + i - 1));
}
}
return dp[m];
};
ll ans = solveLinear(0, n - 1);
ans = min(ans, cost2(n - 1, 0) + solveLinear(1, n - 2));
ans = min(ans, cost3(n - 2, n - 1, 0) + solveLinear(1, n - 3));
ans = min(ans, cost3(n - 1, 0, 1) + solveLinear(2, n - 2));
cout << ans << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
const ll INF = (1LL << 62);
while (t--) {
int n;
cin >> n;
vector<ll> a(n);
for (ll &x : a) cin >> x;
auto cost2 = [&](int i, int j) -> ll {
return llabs(a[i] - a[j]);
};
auto cost3 = [&](int i, int j, int k) -> ll {
ll mn = min({a[i], a[j], a[k]});
ll mx = max({a[i], a[j], a[k]});
return mx - mn;
};
auto solveLinear = [&](int l, int r) -> ll {
if (l > r) return 0LL;
int m = r - l + 1;
vector<ll> dp(m + 1, INF);
dp[0] = 0;
for (int i = 1; i <= m; i++) {
if (i >= 2) {
dp[i] = min(dp[i], dp[i - 2] + cost2(l + i - 2, l + i - 1));
}
if (i >= 3) {
dp[i] = min(dp[i], dp[i - 3] + cost3(l + i - 3, l + i - 2, l + i - 1));
}
}
return dp[m];
};
ll ans = solveLinear(0, n - 1);
ans = min(ans, cost2(n - 1, 0) + solveLinear(1, n - 2));
ans = min(ans, cost3(n - 2, n - 1, 0) + solveLinear(1, n - 3));
ans = min(ans, cost3(n - 1, 0, 1) + solveLinear(2, n - 2));
cout << ans << '\n';
}
}