A 2D prefix sum is a technique used to calculate the sum of elements in any submatrix efficiently. It precomputes the sum of elements from the top-left corner to every cell, allowing each submatrix sum query to be answered in O(1) time.
- It extends the concept of prefix sums from 1D arrays to 2D matrices.
- It uses the inclusion-exclusion principle to avoid repeated calculations.
Example
Input:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16Prefix sum matrix is:
1 3 6 10
6 14 24 36
15 33 54 78
28 60 96 136Each element of the prefix sum matrix represents the sum of all elements in the rectangle from the top-left corner (0, 0) to that position.
- prefix[0][0] = 1
- prefix[0][1] = 1 + 2 = 3
- prefix[0][2] = 1 + 2 + 3 = 6
- prefix[0][3] = 1 + 2 + 3 + 4 = 10
- prefix[1][0] = 1 + 5 = 6
- prefix[1][1] = 1 + 2 + 5 + 6 = 14
- prefix[1][2] = 1 + 2 + 3 + 5 + 6 + 7 = 24
- prefix[1][3] = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36
- prefix[2][0] = 1 + 5 + 9 = 15
- prefix[2][1] = 1 + 2 + 5 + 6 + 9 + 10 = 33
- prefix[2][2] = 1 + 2 + 3 + 5 + 6 + 7 + 9 + 10 + 11 = 54
- prefix[2][3] = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 = 78
- prefix[3][0] = 1 + 5 + 9 + 13 = 28
- prefix[3][1] = 1 + 2 + 5 + 6 + 9 + 10 + 13 + 14 = 60
- prefix[3][2] = 1 + 2 + 3 + 5 + 6 + 7 + 9 + 10 + 11 + 13 + 14 + 15 = 96
- prefix[3][3] = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 = 136
The prefix sum matrix can be constructed using the following approach.
1. Constructing the Prefix Sum Matrix
For every cell (i, j), add the current element, the prefix sum from the top, and the prefix sum from the left. The overlapping top-left region is subtracted once.
The prefix sum for any cell can be calculated using:
prefix[i][j] = arr[i][j]
+ prefix[i-1][j]
+ prefix[i][j-1]
- prefix[i-1][j-1]
The top and left prefix sums are added, while the top-left prefix sum is subtracted because it is counted twice.
For example, for prefix[1][1]:
prefix[1][1]
= arr[1][1] + prefix[0][1] + prefix[1][0] - prefix[0][0]
= 5 + 3 + 5 - 1
= 12
This preprocessing allows us to calculate the sum of any rectangular region of the matrix without traversing all its elements.
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> prefixSum2D(
const vector<vector<int>>& arr)
{
int n = arr.size();
int m = arr[0].size();
vector<vector<int>> prefix(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
prefix[i][j] = arr[i][j];
if (i > 0)
prefix[i][j] += prefix[i - 1][j];
if (j > 0)
prefix[i][j] += prefix[i][j - 1];
if (i > 0 && j > 0)
prefix[i][j] -= prefix[i - 1][j - 1];
}
}
return prefix;
}
int main()
{
vector<vector<int>> arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
vector<vector<int>> prefix = prefixSum2D(arr);
for (const auto& row : prefix) {
for (int value : row)
cout << value << " ";
cout << '\n';
}
return 0;
}
import java.util.ArrayList;
class GfG {
public static ArrayList<ArrayList<Integer>> prefixSum2D(int[][] arr) {
// number of rows
int n = arr.length;
// number of columns
int m = arr[0].length;
// Initialize prefix with 0s
ArrayList<ArrayList<Integer>> prefix = new ArrayList<>();
for (int i = 0; i < n; i++) {
prefix.add(new ArrayList<>());
for (int j = 0; j < m; j++) {
prefix.get(i).add(0);
}
}
// Compute prefix sum matrix
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// Start with original value
int value = arr[i][j];
// Add value from top cell if it exists
if (i > 0) {
value += prefix.get(i - 1).get(j);
}
// Add value from left cell if it exists
if (j > 0) {
value += prefix.get(i).get(j - 1);
}
// Subtract overlap from top-left diagonal if it exists
if (i > 0 && j > 0) {
value -= prefix.get(i - 1).get(j - 1);
}
prefix.get(i).set(j, value);
}
}
return prefix;
}
public static void main(String[] args) {
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
ArrayList<ArrayList<Integer>> prefix = prefixSum2D(arr);
for (ArrayList<Integer> row : prefix) {
for (int val : row) {
System.out.print(val + " ");
}
System.out.println();
}
}
}
def prefixSum2D(arr):
# number of rows
n = len(arr)
# number of columns
m = len(arr[0])
# Initialize prefix with 0s
prefix = [[0] * m for _ in range(n)]
# Compute prefix sum matrix
for i in range(n):
for j in range(m):
# Start with original value
prefix[i][j] = arr[i][j]
# Add value from top cell if it exists
if i > 0:
prefix[i][j] += prefix[i - 1][j]
# Add value from left cell if it exists
if j > 0:
prefix[i][j] += prefix[i][j - 1]
# Subtract overlap from top-left diagonal if it exists
if i > 0 and j > 0:
prefix[i][j] -= prefix[i - 1][j - 1]
return prefix
if __name__ == "__main__":
arr = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]
prefix = prefixSum2D(arr)
for row in prefix:
print(" ".join(map(str, row)))
using System;
using System.Collections.Generic;
class GfG{
public static List<List<int>> PrefixSum2D(int[,] arr){
// number of rows
int n = arr.GetLength(0);
// number of columns
int m = arr.GetLength(1);
// initialize prefix matrix with 0s
List<List<int>> prefix = new List<List<int>>();
for (int i = 0; i < n; i++){
List<int> row = new List<int>();
for (int j = 0; j < m; j++){
row.Add(0);
}
prefix.Add(row);
}
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
// Start with original value
int val = arr[i, j];
// Add value from top cell if it exists
if (i > 0){
val += prefix[i - 1][j];
}
// Add value from left cell if it exists
if (j > 0){
val += prefix[i][j - 1];
}
// Subtract overlap from top-left diagonal if it exists
if (i > 0 && j > 0){
val -= prefix[i - 1][j - 1];
}
prefix[i][j] = val;
}
}
return prefix;
}
static void Main(){
int[,] arr = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 16 }
};
List<List<int>> prefix = PrefixSum2D(arr);
foreach (var row in prefix){
foreach (var val in row){
Console.Write(val + " ");
}
Console.WriteLine();
}
}
}
function prefixSum2D(arr) {
// number of rows
const n = arr.length;
// number of columns
const m = arr[0].length;
// initialize prefix matrix with 0s
const prefix = Array.from({ length: n }, () => Array(m).fill(0));
for (let i = 0; i < n; i++) {
for (let j = 0; j < m; j++) {
// Start with original value
let val = arr[i][j];
// Add value from top cell if it exists
if (i > 0) {
val += prefix[i - 1][j];
}
// Add value from left cell if it exists
if (j > 0) {
val += prefix[i][j - 1];
}
// Subtract overlap from top-left diagonal if it exists
if (i > 0 && j > 0) {
val -= prefix[i - 1][j - 1];
}
prefix[i][j] = val;
}
}
return prefix;
}
// Driver Code
const arr = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
];
const prefix = prefixSum2D(arr);
for (let row of prefix) {
console.log(row.join(" "));
}
Output
1 3 6 10 6 14 24 36 15 33 54 78 28 60 96 136
Explanation
- prefix[i][j] initially stores arr[i][j].
- The prefix sums from the top and left are added.
- The top-left region is included twice, so it is subtracted once.
- This process is repeated for every cell to build the complete prefix sum matrix.
2. Using Prefix Sum to Answer Submatrix Sum Queries
Once the prefix sum matrix is constructed, the sum of any submatrix from (r1, c1) to (r2, c2) can be calculated using inclusion-exclusion.
The formula is:
sum = prefix[r2][c2]
- prefix[r1-1][c2]
- prefix[r2][c1-1]
+ prefix[r1-1][c1-1]
When r1 = 0 or c1 = 0, the corresponding prefix value is treated as 0.
#include <bits/stdc++.h>
using namespace std;
vector<int> prefixSum2D(vector<vector<int>> &mat, vector<vector<int>> &queries)
{
int rows = mat.size(), cols = mat[0].size();
// build prefix sum over rows
for (int i = 1; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
mat[i][j] += mat[i - 1][j];
}
}
// build prefix sum over columns
for (int j = 1; j < cols; j++)
{
for (int i = 0; i < rows; i++)
{
mat[i][j] += mat[i][j - 1];
}
}
vector<int> result;
// process each query using inclusion-exclusion
for (auto &q : queries)
{
int r1 = q[0], c1 = q[1], r2 = q[2], c2 = q[3];
// get the total prefix sum from (0,0) to (r2,c2)
int total = mat[r2][c2];
// subtract the area above the submatrix (if any)
int left = (c1 > 0) ? mat[r2][c1 - 1] : 0;
// subtract the area to the left of the submatrix (if any)
int top = (r1 > 0) ? mat[r1 - 1][c2] : 0;
// add back the top-left overlapping area,
// which was subtracted twice
int overlap = (r1 > 0 && c1 > 0) ? mat[r1 - 1][c1 - 1] : 0;
// final submatrix sum using inclusion-exclusion
int sum = total - left - top + overlap;
result.push_back(sum);
}
return result;
}
int main()
{
vector<vector<int>> mat = {{1, 2, 3}, {1, 1, 0}, {4, 2, 2}};
vector<vector<int>> queries = {{0, 0, 1, 1}, {1, 0, 2, 2}};
vector<int> result = prefixSum2D(mat, queries);
for (int x : result)
{
cout << x << " ";
}
return 0;
}
import java.util.*;
class GFG {
static ArrayList<Integer> prefixSum2D(int[][] mat, int[][] queries)
{
int rows = mat.length, cols = mat[0].length;
// build prefix sum over rows
for (int i = 1; i < rows; i++) {
for (int j = 0; j < cols; j++) {
mat[i][j] += mat[i - 1][j];
}
}
// build prefix sum over columns
for (int j = 1; j < cols; j++) {
for (int i = 0; i < rows; i++) {
mat[i][j] += mat[i][j - 1];
}
}
ArrayList<Integer> result = new ArrayList<>();
// process each query using inclusion-exclusion
for (int[] q : queries) {
int r1 = q[0], c1 = q[1], r2 = q[2], c2 = q[3];
// get the total prefix sum from (0,0) to
// (r2,c2)
int total = mat[r2][c2];
// subtract the area above the submatrix (if
// any)
int left = (c1 > 0) ? mat[r2][c1 - 1] : 0;
// subtract the area to the left of the
// submatrix (if any)
int top = (r1 > 0) ? mat[r1 - 1][c2] : 0;
// add back the top-left overlapping area,
// which was subtracted twice
int overlap = (r1 > 0 && c1 > 0)
? mat[r1 - 1][c1 - 1]
: 0;
// final submatrix sum using inclusion-exclusion
int sum = total - left - top + overlap;
result.add(sum);
}
return result;
}
public static void main(String[] args)
{
int[][] mat
= { { 1, 2, 3 }, { 1, 1, 0 }, { 4, 2, 2 } };
int[][] queries
= { { 0, 0, 1, 1 }, { 1, 0, 2, 2 } };
ArrayList<Integer> result
= prefixSum2D(mat, queries);
for (int x : result) {
System.out.print(x + " ");
}
}
}
def prefixSum2D(mat, queries):
rows = len(mat)
cols = len(mat[0])
# build prefix sum over rows
for i in range(1, rows):
for j in range(cols):
mat[i][j] += mat[i - 1][j]
# build prefix sum over columns
for j in range(1, cols):
for i in range(rows):
mat[i][j] += mat[i][j - 1]
result = []
# process each query using inclusion-exclusion
for q in queries:
r1, c1, r2, c2 = q
# get the total prefix sum from (0,0) to (r2,c2)
total = mat[r2][c2]
# subtract the area above the submatrix (if any)
left = mat[r2][c1 - 1] if c1 > 0 else 0
# subtract the area to the left of the submatrix (if any)
top = mat[r1 - 1][c2] if r1 > 0 else 0
# add back the top-left overlapping area,
# which was subtracted twice
overlap = mat[r1 - 1][c1 - 1] if r1 > 0 and c1 > 0 else 0
# final submatrix sum using inclusion-exclusion
sum = total - left - top + overlap
result.append(sum)
return result
# Driver Code
if __name__ == "__main__":
mat = [
[1, 2, 3],
[1, 1, 0],
[4, 2, 2]
]
queries = [
[0, 0, 1, 1],
[1, 0, 2, 2]
]
result = prefixSum2D(mat, queries)
for x in result:
print(x, end=" ")
using System;
using System.Collections.Generic;
class GFG {
static List<int> prefixSum2D(int[, ] mat, int[, ] queries)
{
int rows = mat.GetLength(0);
int cols = mat.GetLength(1);
// build prefix sum over rows
for (int i = 1; i < rows; i++) {
for (int j = 0; j < cols; j++) {
mat[i, j] += mat[i - 1, j];
}
}
// build prefix sum over columns
for (int j = 1; j < cols; j++) {
for (int i = 0; i < rows; i++) {
mat[i, j] += mat[i, j - 1];
}
}
List<int> result = new List<int>();
// process each query using inclusion-exclusion
for (int i = 0; i < queries.GetLength(0); i++) {
int r1 = queries[i, 0];
int c1 = queries[i, 1];
int r2 = queries[i, 2];
int c2 = queries[i, 3];
// get the total prefix sum from (0,0) to
// (r2,c2)
int total = mat[r2, c2];
// subtract the area above the submatrix (if
// any)
int left = (c1 > 0) ? mat[r2, c1 - 1] : 0;
// subtract the area to the left of the
// submatrix (if any)
int top = (r1 > 0) ? mat[r1 - 1, c2] : 0;
// add back the top-left overlapping area,
// which was subtracted twice
int overlap = (r1 > 0 && c1 > 0)
? mat[r1 - 1, c1 - 1]
: 0;
// final submatrix sum using inclusion-exclusion
int sum = total - left - top + overlap;
result.Add(sum);
}
return result;
}
static void Main()
{
int[, ] mat
= { { 1, 2, 3 }, { 1, 1, 0 }, { 4, 2, 2 } };
int[, ] queries
= { { 0, 0, 1, 1 }, { 1, 0, 2, 2 } };
List<int> result = prefixSum2D(mat, queries);
foreach(int x in result) { Console.Write(x + " "); }
}
}
function prefixSum2D(mat, queries)
{
let rows = mat.length;
let cols = mat[0].length;
// build prefix sum over rows
for (let i = 1; i < rows; i++) {
for (let j = 0; j < cols; j++) {
mat[i][j] += mat[i - 1][j];
}
}
// build prefix sum over columns
for (let j = 1; j < cols; j++) {
for (let i = 0; i < rows; i++) {
mat[i][j] += mat[i][j - 1];
}
}
let result = [];
// process each query using inclusion-exclusion
for (let q of queries) {
let r1 = q[0];
let c1 = q[1];
let r2 = q[2];
let c2 = q[3];
// get the total prefix sum from (0,0) to (r2,c2)
let total = mat[r2][c2];
// subtract the area above the submatrix (if any)
let left = (c1 > 0) ? mat[r2][c1 - 1] : 0;
// subtract the area to the left of the submatrix
// (if any)
let top = (r1 > 0) ? mat[r1 - 1][c2] : 0;
// add back the top-left overlapping area,
// which was subtracted twice
let overlap
= (r1 > 0 && c1 > 0) ? mat[r1 - 1][c1 - 1] : 0;
// final submatrix sum using inclusion-exclusion
let sum = total - left - top + overlap;
result.push(sum);
}
return result;
}
// Driver Code
let mat = [ [ 1, 2, 3 ], [ 1, 1, 0 ], [ 4, 2, 2 ] ];
let queries = [ [ 0, 0, 1, 1 ], [ 1, 0, 2, 2 ] ];
let result = prefixSum2D(mat, queries);
let ans = "";
for (let x of result) {
ans += x + " ";
}
console.log(ans.trim());
Output
5 10
