Problem Restatement
Given B[i] = the number of subarrays [l, r] containing index i where A[i] is the minimum, reconstruct the array A.
Key Insight
For each index i, define:
- left[i]: the leftmost index such that
A[i]is the minimum ofA[left[i]..i] - right[i]: the rightmost index such that
A[i]is the minimum ofA[i..right[i]]
Then:
B[i] = (i - left[i] + 1) * (right[i] - i + 1)
This counts all subarrays [l, r] where left[i] <= l <= i <= r <= right[i].
The problem reduces to determining left[i], right[i], and A[i] for each i from 1 to N.
Algorithm: Monotone Stack with Difference Array
Process indices i = 1, 2, ..., N left to right, maintaining a stack of indices j whose values A[j] are non-decreasing (bottom to top). The stack represents the “active” previous elements whose right boundary still reaches the current position (right[j] >= i).
We maintain a difference array D to track range increment operations. When a chain of stack elements expires (right[j] < i), we pop them one by one but increment the difference array only for the last one popped — the element closest to the bottom of the chain that was blocking the current position. This represents a single “blocking effect ended” event for that expiration. For that last-popped j:
D[left[j]] += 1
D[right[j] + 1] -= 1
The final output is A[k] + prefix_sum(D, k) for each k = 1..N.
Exception: When A[i] == A[j] (equal-value case), after processing i, we immediately pop j without incrementing its range.
Maintaining the Stack Invariant
The stack contains indices j in increasing order where A[j] is non-decreasing. Additionally, any j on the stack whose right[j] < i has already been popped. Thus, the stack top j always satisfies right[j] >= i-1 (its range at least reached i-1, and possibly further).
Processing Index i
Let j be the current stack top after maintenance (step 1 below). We need to decide whether A[i] == A[j] or A[i] > A[j]. We can only have A[i] == A[j] or A[i] > A[j] because if A[i] < A[j], then j should have been popped (since right[j] < i).
Step 1: Pop expired entries, increment only the last one
While right[stack.top()] < i, pop elements from the stack. Keep track of the last element popped (the one closest to the bottom of the chain). After the loop, if a last-popped element was found, increment its range [left[last], right[last]] by 1 in the difference array:
D[left[last]] += 1
D[right[last] + 1] -= 1
The sentinel at index 0 (with right[0] = N) guarantees the stack is never empty, and the sentinel itself is never popped or incremented.
After this step, let j = stack top.
Step 2: Determine left[i] and whether A[i] == A[j] or A[i] > A[j]
Let j = stack top (never empty due to the sentinel at index 0). Two cases:
Case A[i] == A[j]:
- We inherit:
left[i] = left[j]. - To verify this case is valid, check:
(i - left[j] + 1) * (right[j] - i + 1) == B[i]This computes what
B[i]would be ifA[i] == A[j](same value, same range asj), usingleft[i] = left[j]andright[i] = right[j].
Case A[i] > A[j]:
- Set
left[i] = j + 1(the range starts right afterjsinceA[j] < A[i]).
Decision order: Check the A[i] == A[j] case first. If the equality (i - left[j] + 1) * (right[j] - i + 1) == B[i] holds, we take that case. Otherwise, A[i] > A[j].
Step 3: Compute right[i] and A[i]
Once left[i] is determined:
- If
A[i] == A[j]: Setright[i] = right[j], andA[i] = A[j](same value).- Then pop
jfrom the stack WITHOUT incrementing its range (exception to the normal pop behavior).
- Then pop
- If
A[i] > A[j]:- Set
A[i] = A[j] + 1(minimum value strictly greater thanA[j]). - From
B[i] = (i - left[i] + 1) * (right[i] - i + 1), we knowi - left[i] + 1. Therefore:right[i] - i + 1 = B[i] / (i - left[i] + 1) right[i] = i - 1 + B[i] / (i - left[i] + 1) - The sentinel at index 0 with
A[0] = 0handles the base case: when only the sentinel remains,A[i] = 0 + 1 = 1andleft[i] = 0 + 1 = 1.
- Set
Step 4: Push i onto the stack
Push i with its computed left[i], right[i], A[i]. The stack invariant (non-decreasing A) is maintained.
Why A[i] = A[j] + 1 When A[i] > A[j]
We want the smallest valid A[i]. Since A[j] is the smallest value on the stack (well, A[j] is at the top and A is non-decreasing on stack, so A[j] is the largest), setting A[i] = A[j] + 1 ensures A[i] is the minimum in [j+1, right[i]]. Any smaller value would make A[i] <= A[j], contradicting A[i] > A[j]. Any larger value is valid but unnecessary.
Handling Equal Values on the Stack
When A[i] == A[j], we effectively “extend” j’s range to include i. Both indices share the same left and right boundaries. After processing i, j is popped from the stack without its range being incremented. This correctly handles the duplicate-minimum case: both i and j count subarrays in the same maximal range.
How right[i] Is Already Known
right[i] is fully determined the moment i is processed — no deferred computation:
- Case
A[i] == A[j]:right[i] = right[j], which was already determined whenjwas processed. - Case
A[i] > A[j]:right[i] = i - 1 + B[i] / (i - left[i] + 1), computed directly fromB[i]and the already-knownleft[i].
When a stack element j is popped at a later step i (because right[j] < i), its right boundary is simply read from the precomputed right[j] array. The stack maintenance in Step 1 uses this already-known right[j] to decide whether j is still “active” at position i. After Step 1, the remaining stack top j satisfies right[j] >= i, guaranteeing either A[i] == A[j] or A[i] > A[j].
Initial Conditions and Edge Cases
- Sentinel as base case: The sentinel at index 0 (
A[0] = 0,left[0] = 1,right[0] = N) is always on the stack. It naturally handles the first element and any case where all previous real elements expire —left[i] = 0 + 1 = 1,A[i] = 0 + 1 = 1. - right[i] never exceeds N: Since B[i] corresponds to a valid array A, the division
B[i] / (i - left[i] + 1)always yields an integer, and the resultingright[i]will be at most N. - Difference array:
Dis initialized to zero. The final answer for positionkisA[k] + prefix_sum(D, k).
Complexity
- Time: O(N) — each index is pushed once and popped at most once. Difference array operations are O(1) per pop.
- Space: O(N) — the stack, plus arrays for
A,left,right, andD.
Implementation Note: Sentinel
Insert a sentinel element at index 0 with right[0] = N, A[0] = 0, left[0] = 1 before the loop. Since right[0] = N >= i for all i, the sentinel never gets popped — the stack is never empty, so the “stack empty” branch disappears. The equal-value check is only performed when j > 0 (j is a real element, not the sentinel). When the sentinel is the stack top, we always take A[i] > A[j], giving left[i] = 0 + 1 = 1 and A[i] = 0 + 1 = 1 as the base case.
Pseudocode
# Initialize difference array
D[1..N+1] = 0
# Sentinel
right[0] = N
A[0] = 0
left[0] = 1
stack = [0]
for i = 1 to N:
# Step 1: pop expired, increment only the LAST one popped
last_popped = -1
while right[stack.top()] < i:
last_popped = stack.pop()
if last_popped != -1:
D[left[last_popped]] += 1
D[right[last_popped] + 1] -= 1
j = stack.top()
# Step 2: check A[i] == A[j] first
candidate_B = (i - left[j] + 1) * (right[j] - i + 1)
if j > 0 && candidate_B == B[i]:
# Case: A[i] == A[j]
left[i] = left[j]
right[i] = right[j]
A[i] = A[j]
# Pop j without incrementing its range
stack.pop()
else:
# Case: A[i] > A[j]
left[i] = j + 1
A[i] = A[j] + 1
right[i] = i - 1 + B[i] // (i - left[i] + 1)
stack.push(i)
# Compute final answer using prefix sum of difference array
prefix = 0
for k = 1 to N:
prefix += D[k]
output A[k] + prefix
Correctness Argument
-
Stack invariant: The stack always contains indices with non-decreasing
Avalues. When we pushi,A[i] >= A[j](top), so the invariant holds. Popping maintains the invariant since we remove from the top. -
B[i] decomposition: For any index
i, the number of subarrays whereA[i]is the minimum equals(i - left[i] + 1) * (right[i] - i + 1), whereleft[i]andright[i]are the maximal range whereA[i]is the minimum. This is because any subarray[l, r]withleft[i] <= l <= i <= r <= right[i]has minimumA[i]. -
Disambiguation of equal vs greater: When
A[i] == A[j], indexisharesj’s full range:left[i] = left[j],right[i] = right[j]. WhenA[i] > A[j],jblocks the left extension:left[i] = j + 1. These two cases yield differentleft[i]values, hence differentB[i]values. By checking which case producesB[i], we uniquely determine the relationship. -
Minimal
A[i]: SettingA[i] = A[j] + 1whenA[i] > A[j]gives the smallest valid value. Larger values are also valid but produce the sameleft/rightranges and hence the sameB— so the minimal value is a safe choice. -
Difference array increments: When a chain of elements expires at step
i, only the last one popped (the one that was blocking the current position) gets its range incremented. This correctly models a single “blocking effect ended” event per expiration — the innermost blocker in the chain releases its range. The difference array efficiently applies O(1) per expiration event, with the final prefix sum computing the adjusted values. -
Equal-value pop without increment: When
A[i] == A[j],jis immediately popped without incrementing becauseiinheritsj’s full range and value — the blocking chain continues unbroken throughi, so no increment is needed.