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.
After an operation at , every prefix ending at or later has minimum . So the operation's value only depends on the prefix after increasing .
If is not a strict prefix minimum, increasing changes nothing. Some earlier value is already , so is not controlling any prefix minimum.
For , you only need to consider strict suffix maximums. If there is a later position with , then using gives at least as much added value and deletes the array later, so it cannot be worse.
For a useful pair , the new prefix minimum at position is . Find the first position after with value ; until there, the prefix minimum is exactly .
Enumerate strict prefix minima and strict suffix maxima . Stop for this once reaches the previous prefix minimum, because larger suffix maxima are capped and earlier values are dominated. The total number of tested pairs is linear.
The operation has one very loud consequence:
If we choose , then becomes , so every prefix ending at or later contributes .
So the score after the operation is only
where , , and all other values stay the same.
Let
The no-operation score is , and it only matters for .
Useful left endpoints
If is not a strict prefix minimum, then some earlier element is already . Increasing cannot improve any prefix minimum. It is dead weight.
So we only need positions satisfying
For such an , after adding , the new active minimum at position is
It is capped by , because prefixes ending at still include everything before .
Useful right endpoints
If is not a strict suffix maximum, there is some later with .
Using instead is never worse:
So every optimal operation can be replaced by one where is a strict suffix maximum.
Scoring one pair
Fix a strict prefix minimum and a strict suffix maximum .
Prefixes before are unchanged, contributing
Now define
Find the first position after whose value is at most :
If it does not exist, use a sentinel .
From through
the prefix minimum is exactly , so this block contributes
So initially:
If , we are done.
Otherwise, the raised stopped being the minimum before . We need the rest of the prefix-minimum sum until .
Let be the next strict prefix-minimum position after .
For indices after but before , the old blocker was . Once is no longer the minimum, the answer becomes the second prefix minimum: the smallest value in the prefix excluding this current strict prefix-minimum blocker.
Precompute:
Also store its prefix sum .
Let
Then the remaining part is:
Using prefix sums:
That gives the exact value of operation .
Finding pos
Scan from right to left and maintain a monotone stack of suffix-minimum breakpoints. The stack has increasing values, so for a given we binary search the first position to the right with value .
Each tested pair is therefore scored in .
Why the pair count is linear
For a fixed strict prefix minimum , enumerate strict suffix maxima from right to left. Their values strictly increase.
Stop once
After that, is capped at . Any further suffix maximum is to the left, gives the same capped , and deletes earlier, so it is dominated by the pair where we stopped.
For , the number of uncapped suffix maxima tested is at most
plus one capped candidate. Across all strict prefix minima, these drops telescope to at most , since all values are in . For , we may scan all suffix maxima once. Total tested pairs are .
For every tested pair, update
Finally, the answer for required cost at least is
so suffix-max the best array. Set best[0]=pref[n] first to include doing nothing.
Complexity: per test case from the stack binary searches, with total . Memory is .
#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;
while (T--) {
int n;
cin >> n;
vector<int> a(n + 2);
for (int i = 1; i <= n; i++) cin >> a[i];
const int INF = 1e9;
vector<ll> best(n, 0), pref(n + 1, 0), prefSec(n + 1, 0);
vector<int> mn(n + 1, INF), sec(n + 1, INF), nextPref(n + 2, n + 1);
vector<int> suffixMax;
suffixMax.reserve(n);
for (int i = n; i >= 1; i--) {
if (suffixMax.empty() || a[i] > a[suffixMax.back()]) {
suffixMax.push_back(i);
}
}
int curMin = INF;
int lastPref = -1;
for (int i = 1; i <= n; i++) {
sec[i] = sec[i - 1];
if (a[i] < curMin) {
if (lastPref != -1) nextPref[lastPref] = i;
sec[i] = curMin;
curMin = a[i];
lastPref = i;
} else if (a[i] < sec[i]) {
sec[i] = a[i];
}
mn[i] = curMin;
pref[i] = pref[i - 1] + mn[i];
prefSec[i] = prefSec[i - 1] + sec[i];
}
nextPref[lastPref] = n + 1;
best[0] = pref[n];
a[n + 1] = -INF;
vector<int> st;
st.push_back(n + 1);
for (int i = n; i >= 1; i--) {
if (mn[i - 1] > a[i]) {
for (int j : suffixMax) {
if (j <= i) break;
int raisedMin = min(a[i] + a[j], mn[i - 1]);
int l = 0, r = (int)st.size();
while (r - l > 1) {
int mid = (l + r) / 2;
if (a[st[mid]] <= raisedMin) l = mid;
else r = mid;
}
int upto = min(j - 1, st[l] - 1);
ll val = pref[i - 1] + 1LL * raisedMin * (upto - i + 1);
if (upto + 1 < j) {
int split = min(nextPref[i], j);
val += prefSec[split - 1] - prefSec[upto];
val += pref[j - 1] - pref[split - 1];
}
best[j - i] = max(best[j - i], val);
if (a[i] + a[j] >= mn[i - 1]) break;
}
}
while (!st.empty() && a[i] <= a[st.back()]) st.pop_back();
st.push_back(i);
}
for (int d = n - 2; d >= 0; d--) {
best[d] = max(best[d], best[d + 1]);
}
for (int i = 0; i < n; i++) {
cout << best[i] << (i + 1 == n ? '\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;
while (T--) {
int n;
cin >> n;
vector<int> a(n + 2);
for (int i = 1; i <= n; i++) cin >> a[i];
const int INF = 1e9;
vector<ll> best(n, 0), pref(n + 1, 0), prefSec(n + 1, 0);
vector<int> mn(n + 1, INF), sec(n + 1, INF), nextPref(n + 2, n + 1);
vector<int> suffixMax;
suffixMax.reserve(n);
for (int i = n; i >= 1; i--) {
if (suffixMax.empty() || a[i] > a[suffixMax.back()]) {
suffixMax.push_back(i);
}
}
int curMin = INF;
int lastPref = -1;
for (int i = 1; i <= n; i++) {
sec[i] = sec[i - 1];
if (a[i] < curMin) {
if (lastPref != -1) nextPref[lastPref] = i;
sec[i] = curMin;
curMin = a[i];
lastPref = i;
} else if (a[i] < sec[i]) {
sec[i] = a[i];
}
mn[i] = curMin;
pref[i] = pref[i - 1] + mn[i];
prefSec[i] = prefSec[i - 1] + sec[i];
}
nextPref[lastPref] = n + 1;
best[0] = pref[n];
a[n + 1] = -INF;
vector<int> st;
st.push_back(n + 1);
for (int i = n; i >= 1; i--) {
if (mn[i - 1] > a[i]) {
for (int j : suffixMax) {
if (j <= i) break;
int raisedMin = min(a[i] + a[j], mn[i - 1]);
int l = 0, r = (int)st.size();
while (r - l > 1) {
int mid = (l + r) / 2;
if (a[st[mid]] <= raisedMin) l = mid;
else r = mid;
}
int upto = min(j - 1, st[l] - 1);
ll val = pref[i - 1] + 1LL * raisedMin * (upto - i + 1);
if (upto + 1 < j) {
int split = min(nextPref[i], j);
val += prefSec[split - 1] - prefSec[upto];
val += pref[j - 1] - pref[split - 1];
}
best[j - i] = max(best[j - i], val);
if (a[i] + a[j] >= mn[i - 1]) break;
}
}
while (!st.empty() && a[i] <= a[st.back()]) st.pop_back();
st.push_back(i);
}
for (int d = n - 2; d >= 0; d--) {
best[d] = max(best[d], best[d + 1]);
}
for (int i = 0; i < n; i++) {
cout << best[i] << (i + 1 == n ? '\n' : ' ');
}
}
}