Submatrix Sum Queries
Problem
A matrix has r rows and c columns. Then q queries come. Each query has four numbers r1 c1 r2 c2 (counted from 0): print the sum of all numbers in the rectangle from row r1 to row r2 and from column c1 to column c2 (both ends included). Input: the first line is r c, then the r rows, then the line with q, then the q queries.
Constraints
- 1 <= r, c <= 1000
- 1 <= q <= 10^5
Examples
Input
2 2 1 2 3 4 1 0 0 1 1
Output
10
Solutions
One pass builds a table where each cell holds the sum of the rectangle from the top-left corner. After that, each query needs only four table values (inclusion-exclusion), so it takes O(1).
/*
* Problem : Rectangle sums.
* Input : The matrix and the queries.
* Output : One sum per query.
* Approach: 2D prefix sums
* Time : O(r c + q)
* Space : O(r c)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// This method solves the task. It receives the input and returns the answer.
static String[] solve(String[] lines) {
String[] head = lines[0].split(" ");
int rows = Integer.parseInt(head[0]);
int cols = Integer.parseInt(head[1]);
// Step 1: prefix[i][j] is the sum of the rectangle up to (i - 1, j - 1).
// The extra first row and column hold zeros.
long[][] prefix = new long[rows + 1][cols + 1];
for (int i = 1; i <= rows; i++) {
String[] part = lines[i].split(" ");
for (int j = 1; j <= cols; j++) {
prefix[i][j] = Long.parseLong(part[j - 1]) + prefix[i - 1][j] + prefix[i][j - 1] - prefix[i - 1][j - 1];
}
}
int queries = Integer.parseInt(lines[rows + 1]);
String[] out = new String[queries];
for (int k = 0; k < queries; k++) {
String[] q = lines[rows + 2 + k].split(" ");
int r1 = Integer.parseInt(q[0]);
int c1 = Integer.parseInt(q[1]);
int r2 = Integer.parseInt(q[2]) + 1;
int c2 = Integer.parseInt(q[3]) + 1;
// Step 2: Big rectangle, minus the strip above, minus the strip on the left, plus the corner that was removed twice.
out[k] = String.valueOf(prefix[r2][c2] - prefix[r1][c2] - prefix[r2][c1] + prefix[r1][c1]);
}
return out;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(String.join("\n", solve(lines)));
}
}
Hints (2)
- Why is the corner added back?
- What is the cost of one query?