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.
For a fixed candidate median value , turn the array into signs: for values , and for values . A subarray has upper median at least exactly when its sign-sum is nonnegative.
If index is one occurrence of the minimum, the chosen subarray must contain and cannot cross any value smaller than . So compute the maximal interval where can legally be the minimum.
For fixed and median threshold , the best valid sign-sum containing is: best suffix of + contribution of + best prefix of . Empty sides are allowed.
Sweep the possible minimum value upward, while keeping a median candidate that only increases. When the candidate threshold rises by , only positions with that exact value flip from to .
Use a segment tree storing total sum, max prefix, max suffix, and max subarray sum. Then each “can we raise the median?” check is two range queries, and each index flips once total, giving .
The core trick is to stop trying to directly compute medians. Direct medians over all subarrays is a fast ticket to TLE city.
For a candidate value , define a transformed array:
For any subarray, its upper median is at least iff the sum of these values over the subarray is nonnegative.
Why? The sum is
A nonnegative sum means there are at least as many elements as elements . For the upper median, that is exactly enough to force the median to be at least .
Handling the minimum
Suppose we choose index as an occurrence of the minimum of the subarray. Then the subarray must:
So for every index , compute the largest interval containing such that every element in it is at least . This is done with monotonic stacks by finding the nearest strictly smaller element on both sides.
Now fix an index and a median threshold . Inside , the best transformed subarray containing is:
where is the maximum suffix sum and is the maximum prefix sum. Empty left or right parts are allowed, because the subarray might extend only one way.
If this value is at least , then there exists a subarray with minimum exactly and median at least .
The sweep
We process possible minimum values from small to large. Keep a variable , meaning the largest median value we have already reached. We try to prove that is possible.
The segment tree always represents signs for threshold :
Initially , so values equal to are already , and all larger values are .
For every position with , we check whether the best subarray containing inside has nonnegative sum. If yes, then median is achievable with minimum , so we increment . After incrementing, all positions with value exactly med flip to , because the next threshold became .
This may look suspicious because med is global, not reset for each . But it is fine. As increases, the value only gets worse unless med increases. So carrying an old med forward cannot invent a better answer. The only way to improve the answer at the current minimum is to actually raise med, and every raise is verified by a real index of the current minimum.
Segment tree details
Each node stores:
sum: total segment sum,pref: maximum prefix sum, allowing empty prefix,suff: maximum suffix sum, allowing empty suffix,best: maximum subarray sum, allowing empty subarray.For two children and :
The final check for position uses only the left suffix and right prefix, plus the sign of under the current threshold.
Each array position flips from to once. Each query and update costs . Therefore the total complexity is
per test case, with memory.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Node {
int best = 0, pref = 0, suff = 0, sum = 0;
};
Node mergeNode(const Node& a, const Node& b) {
Node c;
c.sum = a.sum + b.sum;
c.pref = max(a.pref, a.sum + b.pref);
c.suff = max(b.suff, a.suff + b.sum);
c.best = max({a.best, b.best, a.suff + b.pref});
return c;
}
struct SegTree {
int n = 0;
vector<Node> st;
SegTree(int n = 0) { init(n); }
void init(int n_) {
n = n_;
st.assign(4 * n + 4, Node());
}
void build(int v, int l, int r) {
if (l == r) {
st[v] = {1, 1, 1, 1};
return;
}
int m = (l + r) / 2;
build(v * 2, l, m);
build(v * 2 + 1, m + 1, r);
st[v] = mergeNode(st[v * 2], st[v * 2 + 1]);
}
void update(int v, int l, int r, int pos, int val) {
if (l == r) {
int x = max(val, 0);
st[v] = {x, x, x, val};
return;
}
int m = (l + r) / 2;
if (pos <= m) update(v * 2, l, m, pos, val);
else update(v * 2 + 1, m + 1, r, pos, val);
st[v] = mergeNode(st[v * 2], st[v * 2 + 1]);
}
Node query(int v, int l, int r, int ql, int qr) {
if (ql > qr) return Node();
if (ql == l && qr == r) return st[v];
int m = (l + r) / 2;
return mergeNode(
query(v * 2, l, m, ql, min(qr, m)),
query(v * 2 + 1, m + 1, r, max(ql, m + 1), qr)
);
}
};
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);
int mx = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
mx = max(mx, a[i]);
}
vector<vector<int>> pos(mx + 1);
for (int i = 1; i <= n; i++) pos[a[i]].push_back(i);
vector<int> L(n + 1), R(n + 1), stk;
a[0] = 0;
stk.push_back(0);
for (int i = 1; i <= n; i++) {
while (stk.back() && a[stk.back()] >= a[i]) stk.pop_back();
L[i] = stk.back() + 1;
stk.push_back(i);
}
a[n + 1] = 0;
stk.clear();
stk.push_back(n + 1);
for (int i = n; i >= 1; i--) {
while (stk.back() != n + 1 && a[stk.back()] >= a[i]) stk.pop_back();
R[i] = stk.back() - 1;
stk.push_back(i);
}
SegTree seg(n);
seg.build(1, 1, n);
int med = 1;
for (int p : pos[1]) seg.update(1, 1, n, p, -1);
int ans = 0;
for (int mn = 1; mn <= mx; mn++) {
for (int p : pos[mn]) {
while (med < mx) {
int left = seg.query(1, 1, n, L[p], p - 1).suff;
int right = seg.query(1, 1, n, p + 1, R[p]).pref;
int mid = (a[p] <= med ? -1 : 1);
if (left + mid + right < 0) break;
med++;
for (int q : pos[med]) seg.update(1, 1, n, q, -1);
}
}
ans = max(ans, med - mn);
}
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Node {
int best = 0, pref = 0, suff = 0, sum = 0;
};
Node mergeNode(const Node& a, const Node& b) {
Node c;
c.sum = a.sum + b.sum;
c.pref = max(a.pref, a.sum + b.pref);
c.suff = max(b.suff, a.suff + b.sum);
c.best = max({a.best, b.best, a.suff + b.pref});
return c;
}
struct SegTree {
int n = 0;
vector<Node> st;
SegTree(int n = 0) { init(n); }
void init(int n_) {
n = n_;
st.assign(4 * n + 4, Node());
}
void build(int v, int l, int r) {
if (l == r) {
st[v] = {1, 1, 1, 1};
return;
}
int m = (l + r) / 2;
build(v * 2, l, m);
build(v * 2 + 1, m + 1, r);
st[v] = mergeNode(st[v * 2], st[v * 2 + 1]);
}
void update(int v, int l, int r, int pos, int val) {
if (l == r) {
int x = max(val, 0);
st[v] = {x, x, x, val};
return;
}
int m = (l + r) / 2;
if (pos <= m) update(v * 2, l, m, pos, val);
else update(v * 2 + 1, m + 1, r, pos, val);
st[v] = mergeNode(st[v * 2], st[v * 2 + 1]);
}
Node query(int v, int l, int r, int ql, int qr) {
if (ql > qr) return Node();
if (ql == l && qr == r) return st[v];
int m = (l + r) / 2;
return mergeNode(
query(v * 2, l, m, ql, min(qr, m)),
query(v * 2 + 1, m + 1, r, max(ql, m + 1), qr)
);
}
};
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);
int mx = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
mx = max(mx, a[i]);
}
vector<vector<int>> pos(mx + 1);
for (int i = 1; i <= n; i++) pos[a[i]].push_back(i);
vector<int> L(n + 1), R(n + 1), stk;
a[0] = 0;
stk.push_back(0);
for (int i = 1; i <= n; i++) {
while (stk.back() && a[stk.back()] >= a[i]) stk.pop_back();
L[i] = stk.back() + 1;
stk.push_back(i);
}
a[n + 1] = 0;
stk.clear();
stk.push_back(n + 1);
for (int i = n; i >= 1; i--) {
while (stk.back() != n + 1 && a[stk.back()] >= a[i]) stk.pop_back();
R[i] = stk.back() - 1;
stk.push_back(i);
}
SegTree seg(n);
seg.build(1, 1, n);
int med = 1;
for (int p : pos[1]) seg.update(1, 1, n, p, -1);
int ans = 0;
for (int mn = 1; mn <= mx; mn++) {
for (int p : pos[mn]) {
while (med < mx) {
int left = seg.query(1, 1, n, L[p], p - 1).suff;
int right = seg.query(1, 1, n, p + 1, R[p]).pref;
int mid = (a[p] <= med ? -1 : 1);
if (left + mid + right < 0) break;
med++;
for (int q : pos[med]) seg.update(1, 1, n, q, -1);
}
}
ans = max(ans, med - mn);
}
cout << ans << '\n';
}
return 0;
}