Appearance
Max GEQ Sum -- CF 1691D
Difficulty: 1800 * Techniques: monotonic stack, sparse table, prefix sums Problem Link
Quick Revisit
- PROBLEM: Check if max(subarray) >= sum(subarray) for every subarray
- KEY INSIGHT: Fix the max element, find its domain via monotonic stack, then check max subarray sum in that domain
- TECHNIQUES: monotonic stack, sparse table (range max/min), prefix sums
- TRANSFER: "For every subarray, property P holds" -- iterate over the controlling element, not over subarrays
First Read
We have an array
My brain immediately goes: "Wait, max of a subarray is always at most the sum when elements are non-negative." So this only gets interesting with negative numbers. The max is always one of the elements. So really, the question is: for every element
That reframing is everything. We don't iterate over subarrays -- we iterate over elements as potential maximums.
Wrong Turn: Sliding Window
My first instinct was some kind of two-pointer or sliding window approach -- expand a window, track max and sum, check the condition. But the max changes discontinuously as the window moves. You can't maintain it efficiently in a plain sliding window. And there are
The Breakthrough
For each element
Once I know the range
Prefix sums + sparse table for range-max queries on prefix sums. That's it.
Building It
- Compute prefix sums
where . - Build a sparse table on
for range-max queries. - Use a monotonic stack to find, for each
, the boundaries and where is the maximum. - For each
: the max subarray sum through in range equals . We need a sparse table for range-max and range-min on prefix sums. - Check if that max subarray sum exceeds
.
Implementation Notes
Tie convention. When two elements are equal, both could claim "I am the max of this subarray." We only want one representative per equal group, otherwise we'd test the same subarray twice with different anchors and could miss subarrays at the boundary. The standard fix is asymmetric: pop on
<(strict) when sweeping for the left boundary and on<=when sweeping for the right (or vice versa). Equal elements get assigned consistently to the leftmost (or rightmost) of the group, so each subarray has exactly one anchor.Prefix-sum indexing.
, and the sum of (1-indexed inclusive) is . In 0-indexed code, the sum of is . When element (0-indexed) is the max on range , the max subarray sum through is In code that becomes
mx.query(i + 1, R[i] + 1) - mn.query(L[i], i). The right query starts ati+1because the subarray must include positioni; the left query ends atibecauseP[i]represents "sum before indexi."We need two sparse tables: one for max of prefix sums, one for min of prefix sums.
Tiny dry run
a = [-1, 2, -1]. Prefix sums
Take 2). Both stack passes give
mx.query(2, 3)== = 1. mn.query(0, 1)== = . - best subarray sum through index 1 =
.
Compare to Yes.
The Code
cpp
#include <bits/stdc++.h>
using namespace std;
struct SparseTable {
vector<vector<long long>> tb;
int flag; // 1 = max, -1 = min
SparseTable() {}
SparseTable(vector<long long>& a, int f) : flag(f) {
int n = a.size(), LOG = __lg(n) + 1;
tb.assign(LOG, vector<long long>(n));
tb[0] = a;
for (int k = 1; k < LOG; k++)
for (int i = 0; i + (1 << k) <= n; i++)
tb[k][i] = (flag == 1) ? max(tb[k-1][i], tb[k-1][i + (1 << (k-1))])
: min(tb[k-1][i], tb[k-1][i + (1 << (k-1))]);
}
long long query(int l, int r) { // [l, r]
int k = __lg(r - l + 1);
return (flag == 1) ? max(tb[k][l], tb[k][r - (1 << k) + 1])
: min(tb[k][l], tb[k][r - (1 << k) + 1]);
}
};
void solve() {
int n; cin >> n;
vector<long long> a(n), pre(n + 1, 0);
for (int i = 0; i < n; i++) { cin >> a[i]; pre[i+1] = pre[i] + a[i]; }
SparseTable mx(pre, 1), mn(pre, -1);
vector<int> L(n), R(n);
stack<int> st;
for (int i = 0; i < n; i++) {
while (!st.empty() && a[st.top()] < a[i]) st.pop();
L[i] = st.empty() ? 0 : st.top() + 1;
st.push(i);
}
while (!st.empty()) st.pop();
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && a[st.top()] <= a[i]) st.pop();
R[i] = st.empty() ? n - 1 : st.top() - 1;
st.push(i);
}
for (int i = 0; i < n; i++) {
long long best = mx.query(i + 1, R[i] + 1) - mn.query(L[i], i);
if (best > a[i]) { cout << "No\n"; return; }
}
cout << "Yes\n";
}
int main() {
int t; cin >> t;
while (t--) solve();
}Transfer Lesson
- "For every subarray, some property holds" -- don't iterate subarrays. Fix the controlling element (the max, the min) and find its domain with a monotonic stack.
- Max subarray sum in a range -- think prefix sums + sparse table for range-max/min, not Kadane's (Kadane doesn't support arbitrary ranges).
- Two sparse tables (max and min of prefix sums) is a common duo for subarray sum queries.
See also: Monotonic Stack | Sparse Table / RMQ | Prefix Sums | Pattern Recognition Guide | Practice Ladder 1700-2100