name: Prefix Sum
slug: prefix-sum
difficulty_level: 2

description: >
  Precompute cumulative sums to answer range sum queries in O(1) time. This
  transforms repeated O(n) range calculations into O(n) preprocessing plus
  O(1) per query, making it essential for problems involving subarray sums.

when_to_use: |
  - Range sum queries
  - Subarray sum equals target
  - Count subarrays with given sum
  - Product of array except self
  - 2D matrix region sums

metaphor: |
  Imagine tracking your total running distance over a year. Instead of adding up
  daily distances each time someone asks "how far did you run from day 50 to day
  75?", you keep a running total. The cumulative distance on day 75 minus day 49
  instantly gives you the answer.

  Another analogy: a bank account balance. To find how much you spent between
  two dates, you subtract the earlier balance from the later balance—no need to
  sum individual transactions.

core_concept: |
  A **prefix sum array** stores cumulative sums where `prefix[i]` = sum of all
  elements from index 0 to i-1. This enables O(1) range sum queries:

  **sum(i, j) = prefix[j+1] - prefix[i]**

  The key insight is that any range sum can be computed from two prefix sums.
  Instead of iterating through the range (O(n) per query), we do O(n) preprocessing
  once and answer unlimited queries in O(1) each.

  This pattern extends to:
  - **Prefix products**: For multiplication-based problems
  - **Prefix counts**: For counting occurrences
  - **2D prefix sums**: For matrix region queries
  - **Prefix sum + hash map**: For "subarray sum equals K"

visualization: |
  **Building prefix sum array:**

  ```
  Array:      [3,  1,  4,  1,  5,  9]
  Index:       0   1   2   3   4   5

  prefix[0] = 0                      (empty prefix)
  prefix[1] = 0 + 3 = 3              (sum of first 1 element)
  prefix[2] = 3 + 1 = 4              (sum of first 2 elements)
  prefix[3] = 4 + 4 = 8
  prefix[4] = 8 + 1 = 9
  prefix[5] = 9 + 5 = 14
  prefix[6] = 14 + 9 = 23

  Prefix:    [0,  3,  4,  8,  9, 14, 23]
  Index:      0   1   2   3   4   5   6
  ```

  **Range sum query: sum(2, 4) = elements at indices 2, 3, 4**

  ```
  sum(2, 4) = prefix[5] - prefix[2]
            = 14 - 4
            = 10

  Verification: arr[2] + arr[3] + arr[4] = 4 + 1 + 5 = 10 ✓
  ```

  **Subarray sum equals K using hash map:**

  ```
  Array: [1, 2, 3, -2, 5]  K = 4

  As we iterate, track prefix sums and look for prefix_sum - K:

  i=0: prefix=1, need 1-4=-3, not found
  i=1: prefix=3, need 3-4=-1, not found
  i=2: prefix=6, need 6-4=2, not found
  i=3: prefix=4, need 4-4=0, found! (empty prefix)
        → subarray [0:4] sums to 4
  i=4: prefix=9, need 9-4=5, not found

  Wait, also: prefix at i=1 is 3, at i=4 is 9-4=5... let me recalculate
  Actually arr[1:3] = 2+3-2=3, arr[2:4]=3-2+5=6... checking for K=4:
  subarray [0:4] → 1+2+3-2=4 ✓
  ```

code_template: |
  def build_prefix_sum(arr: list[int]) -> list[int]:
      """Build prefix sum array. prefix[i] = sum of arr[0:i]."""
      prefix = [0] * (len(arr) + 1)
      for i in range(len(arr)):
          prefix[i + 1] = prefix[i] + arr[i]
      return prefix


  def range_sum(prefix: list[int], i: int, j: int) -> int:
      """Sum of elements from index i to j (inclusive)."""
      return prefix[j + 1] - prefix[i]


  def subarray_sum_equals_k(nums: list[int], k: int) -> int:
      """Count subarrays with sum equal to k."""
      count = 0
      prefix_sum = 0
      # Map: prefix_sum -> count of occurrences
      sum_count = {0: 1}  # Empty prefix has sum 0

      for num in nums:
          prefix_sum += num

          # If (prefix_sum - k) exists, those prefixes form valid subarrays
          if prefix_sum - k in sum_count:
              count += sum_count[prefix_sum - k]

          # Record current prefix sum
          sum_count[prefix_sum] = sum_count.get(prefix_sum, 0) + 1

      return count


  def product_except_self(nums: list[int]) -> list[int]:
      """Product of array except self without division."""
      n = len(nums)
      result = [1] * n

      # Prefix products (left to right)
      prefix = 1
      for i in range(n):
          result[i] = prefix
          prefix *= nums[i]

      # Suffix products (right to left)
      suffix = 1
      for i in range(n - 1, -1, -1):
          result[i] *= suffix
          suffix *= nums[i]

      return result


  def matrix_region_sum(matrix: list[list[int]],
                        row1: int, col1: int,
                        row2: int, col2: int) -> int:
      """2D prefix sum for matrix region queries."""
      # Build 2D prefix sum
      m, n = len(matrix), len(matrix[0])
      prefix = [[0] * (n + 1) for _ in range(m + 1)]

      for i in range(1, m + 1):
          for j in range(1, n + 1):
              prefix[i][j] = (matrix[i-1][j-1]
                              + prefix[i-1][j]
                              + prefix[i][j-1]
                              - prefix[i-1][j-1])

      # Query region sum using inclusion-exclusion
      return (prefix[row2+1][col2+1]
              - prefix[row1][col2+1]
              - prefix[row2+1][col1]
              + prefix[row1][col1])

recognition_signals:
  - "range sum"
  - "subarray sum"
  - "cumulative"
  - "sum equals k"
  - "count subarrays"
  - "product except self"
  - "matrix region sum"
  - "running total"
  - "continuous subarray"

common_mistakes:
  - title: Off-by-one in prefix array indexing
    description: |
      Confusion about whether prefix[i] includes arr[i] or not leads to
      incorrect range sums.
    fix: |
      Convention: `prefix[i]` = sum of first i elements = `arr[0:i]`.
      So `prefix[0] = 0` (empty), and range sum is `prefix[j+1] - prefix[i]`.

  - title: Forgetting the empty prefix for "sum equals K"
    description: |
      Not initializing the hash map with `{0: 1}` misses subarrays starting
      from index 0.
    fix: |
      Always initialize: `sum_count = {0: 1}`. This handles the case where the
      subarray from the beginning has sum K.

  - title: Integer overflow with large sums
    description: |
      Prefix sums can grow very large when array elements are big, causing
      overflow in some languages.
    fix: |
      In Python this isn't an issue. In Java/C++, use `long` for prefix sums
      or check constraints carefully.

  - title: Not handling negative numbers
    description: |
      Prefix sum works with negative numbers, but some may expect only positive
      sums and use wrong optimization (like sliding window).
    fix: |
      Prefix sum handles negatives correctly. But problems like "minimum sum
      subarray of size k" need different approaches when negatives are present.

variations:
  - name: Basic prefix sum
    description: |
      Precompute cumulative sums for O(1) range queries. Foundation for other
      variations.
    example: "Range Sum Query - Immutable, Running Sum of 1d Array"

  - name: Prefix sum with hash map
    description: |
      Track prefix sums in a hash map to find subarrays summing to a target.
      Key insight: `prefix[j] - prefix[i] = target` means subarray [i,j] works.
    example: "Subarray Sum Equals K, Contiguous Array"

  - name: Prefix product
    description: |
      Same idea but with multiplication. Watch for zeros and consider using
      left/right products separately.
    example: "Product of Array Except Self"

  - name: 2D prefix sum
    description: |
      Extend to matrices for O(1) region sum queries. Uses inclusion-exclusion
      principle.
    example: "Range Sum Query 2D, Matrix Block Sum"

  - name: Difference array
    description: |
      Inverse of prefix sum. Store differences to support O(1) range updates.
      Prefix sum of difference array gives original.
    example: "Range Addition, Corporate Flight Bookings"

related_patterns:
  - sliding-window
  - two-pointers

prerequisite_patterns: []
