Skip to main content

2D Prefix Sum

2D Prefix Sum

Introduction & Intuition

The 2D Prefix Sum (also called the summed area table or integral image) extends the 1D prefix sum idea to matrices.

Problem: Given a 2D matrix of size m × n, answer queries of the form:
"What is the sum of all elements in the rectangle with top-left (r1, c1) and bottom-right (r2, c2)?"

Naïve approach: Iterate through every cell in the rectangle — O(m × n) per query.
2D Prefix Sum: Preprocess in O(m × n), then answer each query in O(1).


Building the 2D Prefix Array

Let P[i][j] = sum of all elements in the rectangle from (1,1) to (i,j).

Recurrence:

P[i][j] = arr[i][j]
+ P[i-1][j] (sum above)
+ P[i][j-1] (sum to left)
- P[i-1][j-1] (subtract double-counted top-left rectangle)

This is the inclusion-exclusion principle applied to 2D.


Dry Run — Building the Prefix Matrix

Given matrix (1-indexed):

arr =
[ 1, 2, 3 ]
[ 4, 5, 6 ]
[ 7, 8, 9 ]

Computing P[i][j]:

P[1][1] = 1
P[1][2] = 1 + 2 = 3
P[1][3] = 1 + 2 + 3 = 6

P[2][1] = 1 + 4 = 5
P[2][2] = arr[2][2] + P[1][2] + P[2][1] - P[1][1]
= 5 + 3 + 5 - 1 = 12
P[2][3] = arr[2][3] + P[1][3] + P[2][2] - P[1][2]
= 6 + 6 + 12 - 3 = 21

P[3][1] = 1 + 4 + 7 = 12
P[3][2] = arr[3][2] + P[2][2] + P[3][1] - P[2][1]
= 8 + 12 + 12 - 5 = 27
P[3][3] = arr[3][3] + P[2][3] + P[3][2] - P[2][2]
= 9 + 21 + 27 - 12 = 45

Final prefix matrix:

P =
[ 1, 3, 6 ]
[ 5, 12, 21 ]
[ 12, 27, 45 ]

Range Sum Query — Dry Run

Query: Sum of rectangle from (2,2) to (3,3) → expected = 5 + 6 + 8 + 9 = 28

Formula:

rectSum(r1, c1, r2, c2) = P[r2][c2]
- P[r1-1][c2]
- P[r2][c1-1]
+ P[r1-1][c1-1]

Substituting:

rectSum(2, 2, 3, 3) = P[3][3] - P[1][3] - P[3][1] + P[1][1]
= 45 - 6 - 12 + 1
= 28 ✓

Visualizing the Formula

The formula uses inclusion-exclusion:

┌─────────────────────┐
│ │
│ P[r1-1][c1-1] ───►│ P[r1-1][c2]
│ │
│ ───────────────── │
│ │
│ P[r2][c1-1] ───►│ P[r2][c2] ← this is what we want
└─────────────────────┘

rectSum = P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1]
  • Subtract top region: P[r1-1][c2]
  • Subtract left region: P[r2][c1-1]
  • Add back top-left corner (subtracted twice): P[r1-1][c1-1]

Time & Space Complexity

OperationComplexity
Build 2D prefix matrixO(m × n)
Single rectangle queryO(1)
Total for Q queriesO(m×n + Q)
Space (prefix matrix)O(m × n)

C++ Implementation

#include <iostream>
#include <vector>
using namespace std;

int main() {
int m = 3, n = 3;
vector<vector<int>> arr = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

// Build 2D prefix sum (1-indexed)
vector<vector<long long>> P(m + 1, vector<long long>(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
P[i][j] = arr[i-1][j-1]
+ P[i-1][j]
+ P[i][j-1]
- P[i-1][j-1];
}
}

// Rectangle sum query (1-indexed)
auto rectSum = [&](int r1, int c1, int r2, int c2) -> long long {
return P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1];
};

// Example queries
cout << "Sum of (2,2) to (3,3) = " << rectSum(2, 2, 3, 3) << endl; // 28
cout << "Sum of (1,1) to (2,2) = " << rectSum(1, 1, 2, 2) << endl; // 12
cout << "Sum of (1,1) to (3,3) = " << rectSum(1, 1, 3, 3) << endl; // 45

return 0;
}

Output:

Sum of (2,2) to (3,3) = 28
Sum of (1,1) to (2,2) = 12
Sum of (1,1) to (3,3) = 45

Java Implementation

public class TwoDPrefixSum {

static long[][] buildPrefix(int[][] arr) {
if (arr == null || arr.length == 0 || arr[0].length == 0) {
return new long[0][0];
}
int m = arr.length, n = arr[0].length;
long[][] P = new long[m + 1][n + 1]; // 1-indexed
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
P[i][j] = arr[i-1][j-1]
+ P[i-1][j]
+ P[i][j-1]
- P[i-1][j-1];
}
}
return P;
}

static long rectSum(long[][] P, int r1, int c1, int r2, int c2) {
return P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1];
}

public static void main(String[] args) {
int[][] arr = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
long[][] P = buildPrefix(arr);

System.out.println("Sum of (2,2)-(3,3) = " + rectSum(P, 2, 2, 3, 3)); // 28
System.out.println("Sum of (1,1)-(2,2) = " + rectSum(P, 1, 1, 2, 2)); // 12
System.out.println("Sum of (1,1)-(3,3) = " + rectSum(P, 1, 1, 3, 3)); // 45
}
}

Python Implementation

def build_2d_prefix(arr):
if not arr or not arr[0]:
return []
m, n = len(arr), len(arr[0])
P = [[0] * (n + 1) for _ in range(m + 1)] # 1-indexed
for i in range(1, m + 1):
for j in range(1, n + 1):
P[i][j] = (arr[i-1][j-1]
+ P[i-1][j]
+ P[i][j-1]
- P[i-1][j-1])
return P

def rect_sum(P, r1, c1, r2, c2):
"""Returns sum of rectangle (r1,c1) to (r2,c2) — 1-indexed."""
return P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1]

# Example usage
arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
P = build_2d_prefix(arr)

print(f"Sum of (2,2)-(3,3) = {rect_sum(P, 2, 2, 3, 3)}") # 28
print(f"Sum of (1,1)-(2,2) = {rect_sum(P, 1, 1, 2, 2)}") # 12
print(f"Sum of (1,1)-(3,3) = {rect_sum(P, 1, 1, 3, 3)}") # 45

JavaScript Implementation

function build2DPrefix(arr) {
if (!arr || arr.length === 0 || arr[0].length === 0) {
return [];
}
const m = arr.length, n = arr[0].length;
const P = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
P[i][j] = arr[i-1][j-1]
+ P[i-1][j]
+ P[i][j-1]
- P[i-1][j-1];
}
}
return P;
}

function rectSum(P, r1, c1, r2, c2) {
return P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1];
}

// Example usage
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
const P = build2DPrefix(arr);

console.log(`Sum of (2,2)-(3,3) = ${rectSum(P, 2, 2, 3, 3)}`); // 28
console.log(`Sum of (1,1)-(2,2) = ${rectSum(P, 1, 1, 2, 2)}`); // 12
console.log(`Sum of (1,1)-(3,3) = ${rectSum(P, 1, 1, 3, 3)}`); // 45

Real-World Applications

DomainApplication
Image ProcessingIntegral Image (Viola-Jones face detection)
Game DevelopmentFog-of-war visibility sums over rectangular regions
Geographic DataSum of population/values in a rectangular region
Competitive ProgrammingMaximum sum subrectangle, counting 1s in binary matrices
Machine LearningConvolution operations, feature map aggregations

Classic Problem: Maximum Sum Subrectangle

A well-known application of 2D prefix sums is finding the rectangle with the maximum sum. By fixing two rows and using the 1D Kadane's algorithm on column sums (derived from the prefix matrix), this can be solved in O(m² × n).


Summary

  • 2D Prefix Sum extends 1D prefix sum to matrices using the inclusion-exclusion principle.
  • Build time: O(m × n), query time: O(1) per rectangle.
  • Formula: P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1]
  • Always use 1-indexed arrays to keep the formula uniform.
  • Essential for matrix problems in competitive programming and image processing.