Contribution Counting — Subarray & Subset Aggregation Patterns
Instead of iterating over all subarrays and checking a property, flip the perspective: for each element (or pair, or bit), ask “how many subarrays does this contribute to?” This is often the difference between O(n^2) and O(n).
Quick Navigation: “I need to…”
| I need to… | Technique | Section |
|---|---|---|
| Sum of all subarray sums | Element contribution | 1 |
| Sum of subarray minimums/maximums | Monotonic stack + contribution | 3 |
| Sum of subarray ranges (max - min) | Dual monotonic stack | 4 |
| Count unique chars across subarrays | Per-character contribution with prev/next occurrence | 5 |
| Total hamming distance / XOR sums | Bit-level contribution | 6 |
| Pair contribution across subarrays | Pair containment formula | 7 |
| Sum of subsequence widths | Sorted contribution with powers of 2 | 8 |
| Understand when NOT to use this technique | Limitations | 9 |
Table of Contents
- Core Idea: The Mental Flip
- Foundation: Element Containment Formula
- Monotonic Stack + Contribution: Sum of Subarray Minimums
- Sum of Subarray Ranges: Dual Contribution
- Per-Character Contribution: Unique Characters
- Bit-Level Contribution
- Pair Contribution
- Subsequence Contribution
- When Contribution Counting Does NOT Work
- Pattern Recognition Cheat Sheet
- Problem Roadmap
1. Core Idea: The Mental Flip
Traditional approach:
for each subarray [l..r]:
compute property → accumulate
This is O(n^2) subarrays × O(cost per subarray).
Contribution counting:
for each element/pair/bit:
count how many subarrays it participates in → accumulate
This is O(n) elements × O(1) amortized per element = O(n).
The “amortized” matters. When the per-element count is a closed-form formula (§2, §7, §8) the work really is O(1) each. When you need each element’s span — how far it dominates to the left and right (§3, §4) — you get those spans from a monotonic stack, where a single element can trigger many pops. No element is ever pushed or popped more than once across the whole scan, so the total stays O(n), but any individual step can be O(n).
The Analogy
Imagine counting total tips at a restaurant:
- Subarray approach: Go table by table, sum each table’s tips, then total everything -- slow with many tables.
- Contribution approach: Ask each waiter “how many tables did you serve?” and multiply by their tip rate -- one pass over waiters.
Key Insight
Every aggregate over all subarrays can be decomposed as:
Total = Σ (contribution of each unit × number of subarrays containing that unit)
The “unit” can be an element, a pair, a bit position, a character occurrence -- whatever the problem decomposes into.
★ Insight ─────────────────────────────────────
This is the same summation-order swap that powers edge contribution on trees — here the “bridge” is a subarray boundary instead of a tree edge. Σ_subarrays property = Σ_units (unit's value × subarrays containing it). The swap is legal only when a subarray’s property is a sum of independent per-unit terms (sum, min-as-minimum-indicator, XOR-per-bit, distinctness-per-char). The instant the property depends on a combination of units — “at least k pairs”, “max − min ≤ k” — the terms stop being independent and the swap is invalid (see §9).
─────────────────────────────────────────────────
2. Foundation: Element Containment Formula
Question: For an array of length n, how many subarrays contain element at index i?
A subarray [l..r] contains index i when l <= i and r >= i.
Choices for l: 0, 1, ..., i → (i + 1) choices
Choices for r: i, i+1, ..., n-1 → (n - i) choices
Subarrays containing nums[i] = (i + 1) × (n - i)
Visual:
Array: [a, b, c, d, e] n = 5
^
i = 2
l can be: 0, 1, 2 → 3 choices (i + 1)
r can be: 2, 3, 4 → 3 choices (n - i)
Total subarrays containing c: 3 × 3 = 9
The 9 subarrays: [c], [b,c], [a,b,c], [c,d], [b,c,d], [a,b,c,d],
[c,d,e], [b,c,d,e], [a,b,c,d,e]
This is the building block for everything below.
★ Insight ─────────────────────────────────────
(i+1) × (n-i) is just “count the left endpoints × count the right endpoints” — a subarray is pinned down entirely by its (l, r) pair, and l ranges freely over [0, i] while r ranges over [i, n-1]. Multiplying independent choice-counts is the whole formula. Every harder variant only narrows those ranges: a min-problem clips l to “after the previous smaller” and r to “before the next smaller”; a distinct-char problem clips to “after previous same char” and “before next same char”. Same skeleton left_choices × right_choices — different fences.
─────────────────────────────────────────────────
Application: Sum of All Subarray Sums
Problem: Find the sum of sums of all subarrays.
Brute force: O(n^2) -- enumerate all subarrays.
Contribution: Each nums[i] appears in (i+1) × (n-i) subarrays:
def sum_of_subarray_sums(nums):
n = len(nums)
total = 0
for i in range(n):
total += nums[i] * (i + 1) * (n - i)
return total
O(n). That’s it.
Verification
nums = [1, 2, 3]
Subarrays: [1]=1, [2]=2, [3]=3, [1,2]=3, [2,3]=5, [1,2,3]=6
Total = 1 + 2 + 3 + 3 + 5 + 6 = 20
Contribution:
nums[0]=1: appears in (0+1)×(3-0) = 3 subarrays → 1 × 3 = 3
nums[1]=2: appears in (1+1)×(3-1) = 4 subarrays → 2 × 4 = 8
nums[2]=3: appears in (2+1)×(3-2) = 3 subarrays → 3 × 3 = 9
Total = 3 + 8 + 9 = 20 ✓
3. Monotonic Stack + Contribution: Sum of Subarray Minimums
Problem: LC 907 -- Sum of Subarray Minimums
For each nums[i], find: in how many subarrays is it the minimum?
You need the span where nums[i] dominates as the minimum:
left[i]: number of consecutive elements to the left wherenums[i]is still the min (distance to previous smaller element)right[i]: number of consecutive elements to the right wherenums[i]is still the min (distance to next smaller-or-equal element)
nums: [3, 1, 2, 4]
0 1 2 3
For nums[2] = 2:
left boundary: index 1 (value 1 < 2)
left[2] = 2 - 1 = 1 (only index 2 itself)
right boundary: index 4 (end of array)
right[2] = 4 - 2 = 2 (indices 2, 3)
Subarrays where 2 is minimum: 1 × 2 = 2
→ [2], [2, 4]
Why Strict < on One Side, <= on the Other?
To avoid double counting with duplicates:
nums = [2, 2]
If both use strict <:
nums[0]: left=1, right=2 → 2 subarrays
nums[1]: left=2, right=1 → 2 subarrays
Total = 4, but there are only 3 subarrays!
Subarray [2,2] is counted TWICE (once by each 2).
Fix: Use strict < for previous, and <= for next.
nums[0]: left=1, right=1 → 1 (only [2] at index 0)
nums[1]: left=2, right=1 → 2 ([2] at index 1, and [2,2])
Total = 3 ✓
Implementation
def sumSubarrayMins(nums):
n = len(nums)
MOD = 10**9 + 7
# Previous Less Element (strictly less)
left = [0] * n
stack = []
for i in range(n):
while stack and nums[stack[-1]] >= nums[i]:
stack.pop()
left[i] = i - stack[-1] if stack else i + 1
stack.append(i)
# Next Less-or-Equal Element
right = [0] * n
stack = []
for i in range(n - 1, -1, -1):
while stack and nums[stack[-1]] > nums[i]:
stack.pop()
right[i] = stack[-1] - i if stack else n - i
stack.append(i)
return sum(nums[i] * left[i] * right[i] for i in range(n)) % MOD
Time: O(n), Space: O(n)
Walkthrough
nums = [3, 1, 2, 4]
Previous Less (strict <):
i=0: stack=[], left[0]=0+1=1. stack=[0]
i=1: pop 0 (3>=1), stack=[], left[1]=1+1=2. stack=[1]
i=2: 1<2, stop. left[2]=2-1=1. stack=[1,2]
i=3: 2<4, stop. left[3]=3-2=1. stack=[1,2,3]
left = [1, 2, 1, 1]
Next Less-or-Equal (strict > in pop condition):
i=3: stack=[], right[3]=4-3=1. stack=[3]
i=2: 4>2, pop 3. stack=[], right[2]=4-2=2. stack=[2]
i=1: 2>1, pop 2. stack=[], right[1]=4-1=3. stack=[1]
i=0: 1 not > 3, stop. right[0]=1-0=1. stack=[0,1]
right = [1, 3, 2, 1]
Contributions:
3 × 1 × 1 = 3
1 × 2 × 3 = 6
2 × 1 × 2 = 4
4 × 1 × 1 = 4
Total = 17
Verify: subarrays and their mins:
[3]=3, [1]=1, [2]=2, [4]=4, [3,1]=1, [1,2]=1, [2,4]=2, [3,1,2]=1, [1,2,4]=1, [3,1,2,4]=1
Sum = 3+1+2+4+1+1+2+1+1+1 = 17 ✓
★ Insight ─────────────────────────────────────
The strict-< on one side, <= on the other is the single subtlety that makes duplicate values behave. If both boundaries used strict comparison, a subarray of equal minimums like [2,2] would be claimed by both 2s as “the subarray where I am the minimum” and counted twice. Breaking the tie asymmetrically — “I own the subarray only if I’m the leftmost occurrence of the minimum” (strict on the previous side, non-strict on the next) — assigns every subarray to exactly one owner. This tie-break rule recurs everywhere spans partition a range: it is why NGE/NLE templates flip one comparison, not both.
─────────────────────────────────────────────────
4. Sum of Subarray Ranges: Dual Contribution
Problem: LC 2104 -- Sum of Subarray Ranges
Sum of (max - min) over all subarrays = Σ(max) - Σ(min) over all subarrays.
Apply contribution counting twice: once for minimums (same as LC 907), once for maximums (flip the comparisons).
def subArrayRanges(nums):
n = len(nums)
def contribution(compare_pop, compare_pop_right):
"""
compare_pop: condition to pop for left pass
compare_pop_right: condition to pop for right pass
"""
left = [0] * n
right = [0] * n
stack = []
for i in range(n):
while stack and compare_pop(nums[stack[-1]], nums[i]):
stack.pop()
left[i] = i - stack[-1] if stack else i + 1
stack.append(i)
stack = []
for i in range(n - 1, -1, -1):
while stack and compare_pop_right(nums[stack[-1]], nums[i]):
stack.pop()
right[i] = stack[-1] - i if stack else n - i
stack.append(i)
return sum(nums[i] * left[i] * right[i] for i in range(n))
# Sum of maximums: pop when stack top <= current (finding previous greater)
sum_max = contribution(lambda a, b: a <= b, lambda a, b: a < b)
# Sum of minimums: pop when stack top >= current (finding previous smaller)
sum_min = contribution(lambda a, b: a >= b, lambda a, b: a > b)
return sum_max - sum_min
Time: O(n), Space: O(n)
5. Per-Character Contribution: Unique Characters
Problem: LC 828 -- Count Unique Characters of All Substrings
For each character occurrence, count subarrays where it is unique (appears exactly once).
A character at index j is unique in [l..r] if the previous same character is before l and the next same character is after r.
s = "ABA"
0 1 2
For A at index 0:
prev same char: -1 (none)
next same char: 2
l choices: l in [0, 0] → (-1, 0) exclusive → 0 - (-1) = 1
r choices: r in [0, 1] → (0, 2) exclusive → 2 - 0 = 2
Contribution: 1 × 2 = 2 → subarrays [A], [AB]
For B at index 1:
prev same char: -1 (none)
next same char: 3 (none)
l choices: 1 - (-1) = 2
r choices: 3 - 1 = 2
Contribution: 2 × 2 = 4 → [B], [AB], [BA], [ABA]
For A at index 2:
prev same char: 0
next same char: 3 (none)
l choices: 2 - 0 = 2
r choices: 3 - 2 = 1
Contribution: 2 × 1 = 2 → [BA], [A]
Total = 2 + 4 + 2 = 8
def uniqueLetterString(s):
n = len(s)
# For each char, track indices where it appears
last = {} # char → list of indices
for i, c in enumerate(s):
last.setdefault(c, [-1]).append(i)
for c in last:
last[c].append(n) # sentinel
total = 0
for c in last:
indices = last[c]
for k in range(1, len(indices) - 1):
prev_idx = indices[k - 1]
curr_idx = indices[k]
next_idx = indices[k + 1]
total += (curr_idx - prev_idx) * (next_idx - curr_idx)
return total
Time: O(n), Space: O(n)
Variant: LC 2262 -- Total Appeal of a String
Appeal = number of distinct characters in a substring. Each character contributes 1 to appeal for every subarray where it appears at least once.
For character at index i with previous occurrence at prev:
Subarrays where this occurrence is the FIRST of its character:
l choices: prev+1, prev+2, ..., i → (i - prev)
r choices: i, i+1, ..., n-1 → (n - i)
Contribution: (i - prev) × (n - i)
def appealSum(s):
n = len(s)
prev = {} # char → last seen index
total = 0
for i, c in enumerate(s):
p = prev.get(c, -1)
total += (i - p) * (n - i)
prev[c] = i
return total
O(n) time, O(26) space. Elegantly simple.
★ Insight ─────────────────────────────────────
For character problems the fences are the same-character neighbors, not smaller/greater ones. A char occurrence “owns” a subarray for uniqueness when the previous identical char sits left of l and the next identical char sits right of r → (i − prev) × (next − i). LC 2262 (appeal) is a one-line collapse of this: instead of both neighbors, only prev matters because you count each occurrence as the first of its kind in a subarray, so (i − prev) × (n − i). Recognizing that “unique here” and “first-occurrence here” are the same fencing idea with one fence dropped is what turns a monotonic-stack problem into a single running-dictionary pass.
─────────────────────────────────────────────────
6. Bit-Level Contribution
Problem: LC 477 -- Total Hamming Distance
For each bit position, count elements with that bit set vs unset. Each (set, unset) pair contributes 1 to Hamming distance.
def totalHammingDistance(nums):
total = 0
n = len(nums)
for bit in range(30):
ones = sum(1 for x in nums if x & (1 << bit))
zeros = n - ones
total += ones * zeros
return total
Time: O(30n), Space: O(1)
★ Insight ─────────────────────────────────────
Bits are the cleanest “independent units” of all: a number’s bits never interact, so you decompose the whole problem into 30 (or 64) parallel one-bit problems and sum the answers. For Hamming distance, bit b contributes ones × zeros — every set/unset pair differs in that bit, and pairs are independent across bits. For XOR-of-subarrays the per-bit trick is prefix-parity: a subarray’s bit is 1 iff an odd number of that bit falls inside, i.e. iff the two enclosing prefix parities differ, so the count is count[0] × count[1]. When you see “sum/xor/hamming over all …”, split by bit first — the per-bit subproblem is almost always trivial.
─────────────────────────────────────────────────
Problem: Sum of XOR of All Subarrays
For each bit position b and index i, nums[i]’s bit b contributes to the XOR of subarray [l..r] only if the count of set bits at position b in [l..r] is odd.
Use prefix XOR to count:
def sum_xor_all_subarrays(nums):
total = 0
n = len(nums)
for bit in range(30):
# prefix[i] = XOR of bit b for nums[0..i-1]
# Count prefix values where bit b is 0 vs 1
count = [1, 0] # count[0]=1 for empty prefix
prefix_bit = 0
for num in nums:
prefix_bit ^= (num >> bit) & 1
count[prefix_bit] += 1
# Subarrays with odd count = pairs (prefix=0, prefix=1)
total += count[0] * count[1] * (1 << bit)
return total
Bit-width assumption:
range(30)assumes every value fits in 30 bits (0 ≤ nums[i] < 2^30 ≈ 1.07e9). For larger values (e.g. up to 2^63) widen the loop torange(64).
Trace on [1, 2, 3]
A subarray’s XOR is odd-count-per-bit. Enumerate to get the target, then rebuild it bit by bit.
Subarrays: [1]=1, [2]=2, [3]=3, [1,2]=3, [2,3]=1, [1,2,3]=0
Total XOR sum = 1 + 2 + 3 + 3 + 1 + 0 = 10
Per-bit via prefix XOR (count[v] = how many prefixes have this bit = v; empty prefix seeds count[0]=1). A subarray [l..r] has an odd bit count iff prefix[l] and prefix[r+1] differ, so its contribution count is count[0] × count[1]:
bit 0 (value 1): nums bit0 = [1, 0, 1]
start count=[1,0], prefix=0
1 → prefix=1 count=[1,1]
2 → prefix=1 count=[1,2] (bit0 of 2 is 0, no flip)
3 → prefix=0 count=[2,2]
pairs = count[0]×count[1] = 2×2 = 4 → contributes 4 × 1 = 4
bit 1 (value 2): nums bit1 = [0, 1, 1]
start count=[1,0], prefix=0
1 → prefix=0 count=[2,0] (bit1 of 1 is 0)
2 → prefix=1 count=[2,1]
3 → prefix=0 count=[3,1]
pairs = 3×1 = 3 → contributes 3 × 2 = 6
Total = 4 + 6 = 10 ✓ (matches the brute-force sum)
7. Pair Contribution
For a pair of indices (i, j) with i < j, a subarray [l..r] contains both when l <= i and r >= j:
Count of subarrays containing pair (i, j) = (i + 1) × (n - j)
Application: Total Equal Pairs Across All Subarrays
def total_equal_pairs(nums):
n = len(nums)
from collections import defaultdict
# For fixed j, sum the contribution (i+1) × (n-j) over all previous i
# with nums[i] == nums[j]. Track a running sum of (i+1) per value so each
# j is O(1): total += (running sum of (i+1) for this value) × (n - j).
total = 0
prefix_sum = defaultdict(int) # value → running sum of (i+1)
for j in range(n):
total += prefix_sum[nums[j]] * (n - j)
prefix_sum[nums[j]] += j + 1
return total
Time: O(n), Space: O(n)
8. Subsequence Contribution
Problem: LC 891 -- Sum of Subsequence Widths
Width = max - min of a subsequence. For each element, count subsequences where it’s the max minus where it’s the min.
After sorting: For nums[i] in sorted order:
- Subsequences where
nums[i]is the max: any subset ofnums[0..i-1]plusnums[i]→2^isubsequences - Subsequences where
nums[i]is the min: any subset ofnums[i+1..n-1]plusnums[i]→2^(n-1-i)subsequences
def sumSubseqWidths(nums):
MOD = 10**9 + 7
nums.sort()
n = len(nums)
total = 0
for i in range(n):
# As max: contributes +nums[i] × 2^i
# As min: contributes -nums[i] × 2^(n-1-i)
total += nums[i] * (pow(2, i, MOD) - pow(2, n - 1 - i, MOD))
total %= MOD
return total % MOD
Time: O(n log n) for sort, Space: O(1)
9. When Contribution Counting Does NOT Work
Contribution counting works when contributions are independent and additive. It fails when the problem requires a threshold or interaction between contributions within the same subarray.
| Works (independent, additive) | Does NOT Work (threshold / interaction) |
|---|---|
| “Sum of minimums of all subarrays” | “Count subarrays with at least k equal pairs” |
| “Total equal pairs across all subarrays” | “Count subarrays where min > X” |
| “Sum of XOR of all subarrays” | “Count subarrays with at most k distinct elements” |
| “Total appeal of all substrings” | “Longest subarray where max - min <= k” |
| “Sum of subsequence widths” | “Count subarrays with XOR > target” |
The Dividing Line
Ask: “Can I compute each unit’s contribution independently of other units?”
- YES → Contribution counting. Each element/pair/bit contributes some value × (number of subarrays containing it).
- NO → The answer depends on the combination of elements within a subarray. Use sliding window, DP, or segment tree instead.
The “At Least k” Trap
For LC 2537 (Count Good Subarrays, at least k equal pairs):
You CAN count total equal pairs across ALL subarrays using contribution: each pair (i,j) appears in (i+1) × (n-j) subarrays.
But you CANNOT count subarrays with at least k pairs, because that requires knowing how many pairs are simultaneously inside each specific subarray. The contributions interact -- you need sliding window for that.
★ Insight ─────────────────────────────────────
The litmus test: does the property factor into a sum of per-unit terms, or does it involve a comparison/threshold across units? “Total equal pairs” factors — each pair (i,j) contributes its containment count regardless of the others, so contribution counting works. “At least k equal pairs” does not factor — whether a subarray qualifies depends on the joint count of all its pairs, an interaction. Same trap hides in “min > X” (needs the whole window’s min), “≤ k distinct” (needs the window’s distinct set), “range ≤ k” (needs both extremes at once). Additive → contribution counting, O(n). Threshold/interaction → sliding window / DP / segment tree. Misreading this is the one way to apply the technique to a problem it silently gets wrong.
─────────────────────────────────────────────────
10. Pattern Recognition Cheat Sheet
| Signal in Problem | Technique | Example |
|---|---|---|
| “Sum of [property] over ALL subarrays” | Element contribution (i+1)×(n-i) |
Sum of subarray sums |
| “Sum of min/max over all subarrays” | Monotonic stack + contribution | LC 907, LC 2104 |
| “Unique/distinct characters in all substrings” | Per-char with prev/next occurrence | LC 828, LC 2262 |
| “Total hamming distance” / bit aggregates | Bit-level contribution | LC 477 |
| “Sum of pairs across all subarrays” | Pair formula (i+1)×(n-j) |
Total equal pairs |
| “Subsequence widths / ranges” | Sort + power-of-2 counting | LC 891 |
| “How many subarrays contain element X?” | Direct formula (i+1)×(n-i) |
Sum of odd-length sums |
| “Appeal / distinctness across substrings” | Last-occurrence contribution | LC 2262 |
Decision Flowchart
Problem asks for aggregate over ALL subarrays?
├── YES → Can each element/pair contribute independently?
│ ├── YES → CONTRIBUTION COUNTING
│ │ ├── Involves min/max? → + Monotonic Stack
│ │ ├── Involves distinct chars? → + Last occurrence tracking
│ │ └── Involves bits? → + Bit decomposition
│ └── NO (threshold / k constraint) → Sliding Window or DP
└── NO (single subarray: longest, count satisfying X) → Sliding Window / DP / Binary Search
11. Problem Roadmap
Tier 1 -- Direct Element Contribution
| # | Problem | Key Technique |
|---|---|---|
| 1 | LC 1588 -- Sum of All Odd Length Subarrays | nums[i] × count_of_odd_length_subarrays_containing_i |
| 2 | LC 1748 -- Sum of Unique Elements | Degenerate warm-up: each element’s “contribution” is itself iff it occurs once (no span/count math) |
Tier 2 -- Monotonic Stack + Contribution
| # | Problem | Key Technique |
|---|---|---|
| 3 | LC 907 -- Sum of Subarray Minimums | PLE/NLE + contribution (the canonical problem) |
| 4 | LC 2104 -- Sum of Subarray Ranges | Dual contribution: sum_max - sum_min |
| 5 | LC 1856 -- Maximum Subarray Min-Product | Monotonic stack span × prefix sum |
Tier 3 -- Per-Character / Occurrence Contribution
| # | Problem | Key Technique |
|---|---|---|
| 6 | LC 828 -- Count Unique Characters of All Substrings | prev/next same char → span product |
| 7 | LC 2262 -- Total Appeal of a String | Last occurrence tracking, O(n) one-pass |
Tier 4 -- Bit-Level & Pair Contribution
| # | Problem | Key Technique |
|---|---|---|
| 8 | LC 477 -- Total Hamming Distance | Per-bit: ones × zeros |
| 9 | LC 1863 -- Sum of All Subset XOR Totals | Each bit set in 2^(n-1) subsets |
| 10 | LC 2425 -- Bitwise XOR of All Pairings | Contribution counting on bits |
Tier 5 -- Advanced Contribution
| # | Problem | Key Technique |
|---|---|---|
| 11 | LC 891 -- Sum of Subsequence Widths | Sort + contribution via powers of 2 |
| 12 | LC 2681 -- Power of Heroes | Sorted contribution with prefix sums |
| 13 | LC 1498 -- Number of Subsequences That Satisfy the Given Sum | Sort + two pointers + power contribution |
Summary
The core skill is always the same: stop thinking about subarrays, start thinking about what each element (or pair, or bit) contributes to the final answer.
Total = Σ (value of unit) × (number of subarrays/subsets containing that unit)
THREE MOVES BEHIND EVERY CONTRIBUTION PROBLEM
1. PICK THE UNIT Element? pair? bit? character occurrence? The unit is
whatever makes the property a SUM of independent terms.
2. FENCE THE SPAN Count subarrays/subsets containing the unit:
plain element → (i+1) × (n-i)
as minimum → (i-PLE) × (NLE-i) [monotonic stack]
as distinct char → (i-prev) × (next-i) [last occurrence]
across a pair → (i+1) × (n-j)
in a subset → powers of 2 after sorting
3. CHECK ADDITIVITY Does the property factor into per-unit terms? YES → sum
value×span, O(n). NO (threshold / "at least k" / range≤k)
→ wrong tool; use sliding window / DP / segment tree.
Master the containment formula (i+1) × (n-i), learn to combine it with monotonic stacks for min/max problems and with last-occurrence tracking for character problems, and you’ll solve an entire class of “aggregate over all subarrays” problems in O(n).
Self-Test
Answer these from memory, out loud or on paper, before looking. Recognition is not recall: rereading an explanation feels like knowing, and it is not. A question you cannot answer cold names the exact section to revisit — you do not need to reread the guide.
1. State the summation swap in one line, and the condition that makes it legal.
Answer
Σ_over_subarrays f(subarray) = Σ_over_units (unit's value × how many subarrays contain it). Legal only when a subarray’s value is a sum of independent per-unit terms. The moment the property depends on a combination of units (“max − min ≤ k”, “at least k pairs”), the terms interact and the swap is invalid.
2. How many subarrays contain index i in an array of length n, and why?
Answer
(i + 1) × (n − i). There are i + 1 choices of left endpoint (0 through i) and n − i choices of right endpoint (i through n − 1), chosen independently.
3. For “sum of subarray minimums”, what does each element need, and how do you get it?
Answer
The span over which it is the minimum: distance to the previous smaller element on the left and the next smaller on the right. A monotonic stack produces both in O(n) total. The element then contributes value × left_span × right_span.
4. Why must the tie-breaking be asymmetric in that problem?
Answer
With duplicates, a symmetric rule makes two equal elements each claim the subarrays spanning both — counted twice. Making one side strict and the other non-strict assigns every subarray to exactly one owner. This is the bug that only shows up on inputs with repeats.
5. How does per-bit decomposition turn an XOR-sum problem into counting?
Answer
Bits are independent under XOR, so handle each of the 32 positions separately. For bit b, count how many subarrays have an odd number of set bits at that position; each contributes 2^b. Sum over bits. One hard problem becomes 32 easy independent ones.
6. Give a problem where contribution counting does not apply, and say why.
Answer
“Count subarrays where max − min ≤ k.” No element has an independent contribution — whether a subarray qualifies depends on two elements jointly, and their interaction is the whole constraint. That is a sliding-window or monotonic-deque problem.
7. Is the per-element cost really O(1)?
Answer
Only when it is a closed-form formula. When you need spans from a monotonic stack it is amortized O(1) — one element can trigger many pops, but each index is pushed and popped once so the total stays O(n).
See Also
- Stack & Queue §2 — the monotonic stack primitive that §3-4 depend on. Learn it there first if the strict-vs-non-strict tie-breaking feels arbitrary.
- Edge Contribution & Rerooting — the same mental flip on a tree: count what each edge contributes instead of enumerating node pairs.
- Bitmask Techniques — background for the per-bit decomposition in §6.
- Prefix Sum §7 — prefix XOR, the tool §6 uses to make each bit’s count O(n).
- Sliding Window — where to go when §9 tells you contribution counting does not apply, because the constraint is a threshold over the window rather than an independent per-element property.
- Pattern Decision Map — the router: which technique does a cold problem call for?
- Pattern Mastery Program — the spaced-repetition schedule, mastery checklist, and drill formats that turn reading into recall.
Pattern mastered — stop enumerating subarrays; count what each unit contributes and let the total assemble itself.