Codeforces Round 1133 (Div. 1, Div. 2) is coming soon! Register here. ×

Shortest Subarray With Sum at Least K: A Monotonic Stack + Binary Search Approach

Revision en1, by dtu1141b, 2026-07-28 23:24:39

The standard solution to this problem uses a monotonic deque and runs in

Unable to parse markup [type=CF_MATHJAX]

$.

This post describes a different approach running in

Unable to parse markup [type=CF_MATHJAX]

$. Instead of maintaining a deque, we store the useful prefix sums in a monotonic vector and binary-search over it.

The whole idea comes from two simple observations:

  1. A smaller prefix sum is always better.
  2. If two prefix sums are equally good, the later index is always better.

Prefix Sum Formulation

Let

Unable to parse markup [type=CF_MATHJAX]

$ P_0=0,\qquad P_i=a_1+a_2+\cdots+a_i.

Unable to parse markup [type=CF_MATHJAX]

$

Then the sum of the subarray

Unable to parse markup [type=CF_MATHJAX]

$ equals

Unable to parse markup [type=CF_MATHJAX]

$ P_i-P_j.

Unable to parse markup [type=CF_MATHJAX]

$

We want

Unable to parse markup [type=CF_MATHJAX]

$ P_i-P_j\ge K,

Unable to parse markup [type=CF_MATHJAX]

$

or equivalently,

Unable to parse markup [type=CF_MATHJAX]

$ P_j\le P_i-K.

Unable to parse markup [type=CF_MATHJAX]

$

Therefore, for every right endpoint

Unable to parse markup [type=CF_MATHJAX]

$, we need the largest index

Unable to parse markup [type=CF_MATHJAX]

$ satisfying

Unable to parse markup [type=CF_MATHJAX]

$ P_j\le P_i-K.

Unable to parse markup [type=CF_MATHJAX]

$

Choosing the largest valid

Unable to parse markup [type=CF_MATHJAX]

$ minimizes the subarray length

Unable to parse markup [type=CF_MATHJAX]

$.

Removing Dominated Prefix Sums

Suppose

Unable to parse markup [type=CF_MATHJAX]

$ x<y

Unable to parse markup [type=CF_MATHJAX]

$

and

Unable to parse markup [type=CF_MATHJAX]

$ P_y\le P_x.

Unable to parse markup [type=CF_MATHJAX]

$

Then the prefix

Unable to parse markup [type=CF_MATHJAX]

$ is never useful again.

Indeed, if some future endpoint

Unable to parse markup [type=CF_MATHJAX]

$ satisfies

Unable to parse markup [type=CF_MATHJAX]

$ P_i-P_x\ge K,

Unable to parse markup [type=CF_MATHJAX]

$

then

Unable to parse markup [type=CF_MATHJAX]

$ P_i-P_y \ge P_i-P_x \ge K,

Unable to parse markup [type=CF_MATHJAX]

$

because

Unable to parse markup [type=CF_MATHJAX]

$.

Moreover,

Unable to parse markup [type=CF_MATHJAX]

$ i-y<i-x,

Unable to parse markup [type=CF_MATHJAX]

$

so using

Unable to parse markup [type=CF_MATHJAX]

$ always produces a shorter subarray.

Hence, whenever a new prefix sum arrives, every previous prefix sum that is greater than or equal to it can be removed permanently.

[code=cpp] while (!a.empty() && a.back().first >= p[i]) { a.pop_back(); } a.push_back({p[i], i}); [/code]

After every insertion, the stored pairs satisfy

Unable to parse markup [type=CF_MATHJAX]

$ P_{j_1}<P_{j_2}<\cdots<P_{j_m},

Unable to parse markup [type=CF_MATHJAX]

$

and their indices also satisfy

Unable to parse markup [type=CF_MATHJAX]

$ j_1<j_2<\cdots<j_m.

Unable to parse markup [type=CF_MATHJAX]

$

Thus the vector is strictly increasing in both coordinates.

Binary Search

For the current endpoint

Unable to parse markup [type=CF_MATHJAX]

$, define

Unable to parse markup [type=CF_MATHJAX]

$ T=P_i-K.

Unable to parse markup [type=CF_MATHJAX]

$

We need the last stored pair whose prefix sum is at most

Unable to parse markup [type=CF_MATHJAX]

$.

Since prefix sums are strictly increasing, every valid pair forms a prefix of the vector.

Since indices are also strictly increasing, the last valid pair has the largest possible index, exactly the one we want.

Therefore we simply do

[code=cpp] int pos = upper_bound( a.begin(), a.end(), make_pair(p[i] — k, i) ) — a.begin() — 1; [/code]

Searching for the pair

Unable to parse markup [type=CF_MATHJAX]

$ ensures that every stored prefix sum satisfying

Unable to parse markup [type=CF_MATHJAX]

$ is included.

Why Does This Work?

Lemma 1

If

Unable to parse markup [type=CF_MATHJAX]

$ and

Unable to parse markup [type=CF_MATHJAX]

$, then

Unable to parse markup [type=CF_MATHJAX]

$ can be removed forever.

Proof.

Whenever

Unable to parse markup [type=CF_MATHJAX]

$ forms a valid subarray with some future endpoint,

Unable to parse markup [type=CF_MATHJAX]

$ also forms a valid one because its prefix sum is no larger. Since

Unable to parse markup [type=CF_MATHJAX]

$, it always produces a shorter subarray. Hence

Unable to parse markup [type=CF_MATHJAX]

$ is never optimal again. ▢

Lemma 2

The maintained vector is strictly increasing in both prefix sums and indices.

Proof.

Indices are inserted from left to right. Before inserting a new prefix sum, all larger (or equal) suffix prefix sums are removed. Therefore both coordinates remain strictly increasing. ▢

Theorem

For every endpoint

Unable to parse markup [type=CF_MATHJAX]

$, the binary search returns the shortest valid subarray ending at

Unable to parse markup [type=CF_MATHJAX]

$.

Proof.

All feasible prefix sums form a prefix of the vector. Binary search returns the last element of this prefix, which has the largest valid index.

By Lemma 1, every removed prefix is dominated by another one, so deleting it never removes an optimal answer.

Taking the minimum over all endpoints therefore gives the globally shortest valid subarray. ▢

Implementation

[code=cpp] for (int i = 0; i <= n; i++) {

while (!a.empty() && a.back().first >= p[i]) {
    a.pop_back();
}

a.push_back({p[i], i});

if (i == 0) continue;

int pos = upper_bound(
    a.begin(),
    a.end(),
    make_pair(p[i] - k, i)
) - a.begin() - 1;

if (pos == -1) continue;

ans = min(ans, i - a[pos].second);

} [/code]

Here:

  • p is the prefix sum array.
  • a stores the non-dominated pairs

Unable to parse markup [type=CF_MATHJAX]

$.

For the original problem,

Unable to parse markup [type=CF_MATHJAX]

$, so inserting

Unable to parse markup [type=CF_MATHJAX]

$ before querying cannot create a zero-length answer because

Unable to parse markup [type=CF_MATHJAX]

$ P_i\not\le P_i-K.

Unable to parse markup [type=CF_MATHJAX]

$

Complexity

Each prefix sum is

  • inserted once,
  • removed at most once.

Thus all stack operations together take

Unable to parse markup [type=CF_MATHJAX]

$.

We perform one binary search for every prefix, so the total complexity is

Unable to parse markup [type=CF_MATHJAX]

$ O(n\log n).

Unable to parse markup [type=CF_MATHJAX]

$

Memory complexity is

Unable to parse markup [type=CF_MATHJAX]

$ O(n).

Unable to parse markup [type=CF_MATHJAX]

$

Final Perspective

The key object is not really a stack or a deque—it is the set of non-dominated prefix states.

A state

Unable to parse markup [type=CF_MATHJAX]

$ is dominated by

Unable to parse markup [type=CF_MATHJAX]

$ whenever

Unable to parse markup [type=CF_MATHJAX]

$ P_y\le P_x \quad\text{and}\quad y>x.

Unable to parse markup [type=CF_MATHJAX]

$

The second state is simultaneously

  • easier to satisfy (smaller prefix sum),
  • better for minimizing the answer (later index).

Maintaining only these non-dominated states creates a monotone frontier that is increasing in both prefix sum and index. The monotonic deque solution exploits the same dominance relation to achieve

Unable to parse markup [type=CF_MATHJAX]

$, while this approach replaces deque queries with binary search, yielding a simpler

Unable to parse markup [type=CF_MATHJAX]

$ alternative.

Tags stack, binary search, monotonic stack

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en4 English dtu1141b 2026-07-28 23:36:55 0 Tiny change: '\nThe standa' -> 'The standa'
en3 English dtu1141b 2026-07-28 23:35:09 1661 Tiny change: 'i], i});\n````\n\nAfter ' -> 'i], i});\n\nAfter ' (published)
en2 English dtu1141b 2026-07-28 23:29:22 2264
en1 English dtu1141b 2026-07-28 23:24:39 5384 Initial revision (saved to drafts)