↓Skip to main content
  1. LeetCode/

LeetCode 327: Count of Range Sum

·3 mins· ·
LeetCode Hard Merge-Sort Divide-and-Conquer Prefix-Sum Binary-Indexed-Tree
Wei Yi Chung
Author
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
Table of Contents

Meta Data
#

Difficulty: hard First Attempt: 2026-08-01

  • Submissions: AC (08-01) → AC ×5 (09-01) → AC ×4 (09-27) → WA ×2 → AC (10-18) → AC (10-19, half-open rewrite)

Approach
#

Count the subarrays whose sum lies in [lower, upper].

With prefix sums P, the subarray [i, j) sums to P[j] - P[i], so the question becomes:

how many pairs i < j satisfy lower <= P[j] - P[i] <= upper

That is a “count pairs with i < j” problem, so run merge sort counting on the prefix array: before merging, for each P[i] in the left half, the valid P[j] in the right half form a contiguous range, bounded by two pointers that only move right.

The full derivation is in Merge Sort and Divide-and-Conquer Counting.

My Practice Log
#

I retyped this one many times:

  • 08-01: first write-up, closed interval [start, end], accepted on the first try.
  • 09-01, 09-27: retyped and resubmitted the same solution 9 more times.
  • 10-18: renamed the parameters to (i, j), which clashed with the loop variable i, giving two WAs in a row:
    • The recursive call became countAndMergeSort(0, mid), with the wrong left start.
    • The for i in range(...) loop was removed, so only one i was counted.
  • 10-19: rewrote it in the half-open [low, high) style from my merge sort practice ladder, accepted on the first try.

Do not reuse a loop variable’s name for a parameter. That kind of bug raises no error and just gives wrong answers.

Solution (half-open)
#

class Solution:
    def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:
        n = len(nums)
        prefix = [0] * (n + 1)
        for i in range(n):
            prefix[i + 1] = prefix[i] + nums[i]

        def mergeAndSort(low: int, high: int) -> int:
            if high - low <= 1:
                return 0
            mid = (low + high) // 2
            count = mergeAndSort(low, mid) + mergeAndSort(mid, high)

            j1 = j2 = mid
            for i in range(low, mid):
                while j1 < high and prefix[j1] - prefix[i] < lower:
                    j1 += 1                       # first position >= lower
                while j2 < high and prefix[j2] - prefix[i] <= upper:
                    j2 += 1                       # first position > upper
                count += j2 - j1

            prefix[low:high] = sorted(prefix[low:high])
            return count

        return mergeAndSort(0, n + 1)

Note the recursion covers [0, n + 1): the prefix array has n + 1 elements.

Alternative: BIT with coordinate compression
#

Scan prefix sums left to right; for each P[j], query how many earlier P[i] fall in [P[j] - upper, P[j] - lower], then add P[j] to the BIT. Prefix sums are large, so compress them first and convert the query bounds to ranks with bisect.

Complexity
#

Time O(n log n), space O(n).

Mistakes to Avoid
#

  • A parameter name that clashes with a loop variable.
  • Starting the left recursive half at 0.
  • The prefix array has n + 1 elements, so the half-open recursion covers [0, n + 1).
  • Flipping the bounds: P[i] must lie in [P[j] - upper, P[j] - lower], with upper on the left.

Interview Explanation
#

I build prefix sums, so each subarray sum is a difference of two prefix sums, and the task becomes counting pairs i < j with lower <= P[j] - P[i] <= upper. I merge sort the prefix array: before merging, both halves are sorted, and for each P[i] on the left the valid P[j] on the right form a contiguous range, found with two right-moving pointers. Each level is O(n), O(n log n) total. A BIT with coordinate compression also works.

Related

LeetCode 3548: Equal Sum Grid Partition II
·3 mins
LeetCode Hard Prefix-Sum Hash-Table Matrix Enumeration
LeetCode 3548 note: try each horizontal cut (transpose for vertical); the difference must equal a cell on the larger side, and a one-row or one-column side can only drop an end cell, while any cell works otherwise
LeetCode 2382: Maximum Segment Sum After Removals
·2 mins
LeetCode Hard Union-Find Prefix-Sum Ordered-Set Array
LeetCode 2382 note: work backward so removals become additions; the Union-Find root stores the segment sum, and the maximum only grows
LeetCode 2302: Count Subarrays With Score Less Than K
·2 mins
LeetCode Hard Sliding-Window Prefix-Sum Binary-Search Array
LeetCode 2302 note: score = sum × length with positive numbers grows with the window; shrink while the score is >= k, and right - left + 1 valid subarrays end at right
LeetCode 2968: Apply Operations to Maximize Frequency Score
·2 mins
LeetCode Hard Sliding-Window Sorting Prefix-Sum Binary-Search Array
LeetCode 2968 note: after sorting, the cheapest common target for a block is its median; prefix sums give the cost in O(1), and it grows with the window, so slide
LeetCode 2106: Maximum Fruits Harvested After at Most K Steps
·2 mins
LeetCode Hard Sliding-Window Prefix-Sum Binary-Search Array
LeetCode 2106 note: the harvest is always a contiguous block of fruits; covering [L, R] takes (R - L) + min(|start - L|, |R - start|) steps, so slide a window over the fruits array
LeetCode 2102: Sequentially Ordinal Rank Tracker
·3 mins
LeetCode Hard Heap Design Two-Heaps
LeetCode 2102 note: two heaps for “top k” and “the rest”, with a custom lt for reversed string order