N×N개의 수가 N×N 크기의 표에 채워져 있다. (x1, y1)부터 (x2, y2)까지 합을 구하는 프로그램을 작성하시오. (x, y)는 x행 y열을 의미한다.
예를 들어, N = 4이고, 표가 아래와 같이 채워져 있는 경우를 살펴보자.
| 1 | 2 | 3 | 4 |
|---|---|---|---|
| 2 | 3 | 4 | 5 |
| 3 | 4 | 5 | 6 |
| 4 | 5 | 6 | 7 |
여기서 (2, 2)부터 (3, 4)까지 합을 구하면 3+4+5+4+5+6 = 27이고, (4, 4)부터 (4, 4)까지 합을 구하면 7이다.
표에 채워져 있는 수와 합을 구하는 연산이 주어졌을 때, 이를 처리하는 프로그램을 작성하시오.
첫째 줄에 표의 크기 N과 합을 구해야 하는 횟수 M이 주어진다. (1 ≤ N ≤ 1024, 1 ≤ M ≤ 100,000) 둘째 줄부터 N개의 줄에는 표에 채워져 있는 수가 1행부터 차례대로 주어진다. 다음 M개의 줄에는 네 개의 정수 x1, y1, x2, y2 가 주어지며, (x1, y1)부터 (x2, y2)의 합을 구해 출력해야 한다. 표에 채워져 있는 수는 1,000보다 작거나 같은 자연수이다. (x1 ≤ x2, y1 ≤ y2)
총 M줄에 걸쳐 (x1, y1)부터 (x2, y2)까지 합을 구해 출력한다.
처음엔 주어진 이중배열을 가로방향으로 누적합해 더하는 방식으로 해결하려 했다. 아래 그림은 예시에 주어진 배열과 그 배열을 가로 방향으로 누적합한 배열이다.

이 누적합한 배열에서 (2, 2) 에서 (3, 4) 까지의 합, 즉 아래의 분홍색 영역의 합을 구하려면, 오른쪽 누적합 배열에서 파란 부분의 합 - 초록 부분의 합 으로 구할 수 있다.

하지만 이렇게 풀었더니 시간 초과가 났다. 아마 마지막 for문에서 시간복잡도가 O(N * M)이 되서 그런것 같다. 이걸 좀 더 줄일 수 있는 방법이 없을까?
고민하다가 가로 방향으로만 합하지 말고 세로방향까지 모두 누적합한 배열을 구한 후, 답을 반복문을 통해서가 아닌 단순히 배열의 값을 가져오는 걸로 구현하면 어떨까 생각해 보았다.
아래 그림의 오른쪽 배열은 가로 세로 방향 모두 누적합한 배열이다. 분홍색 영역의 합은 파란색 - 초록부분의 합 + 보라색 으로 구할 수 있다.

for문에서 for문을 한번 더 쓸 필요 없이 식 하나로 구할 수 있다.// 시간초과 난 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Baekjoon_11660 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] input = br.readLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
int[][] arr = new int[n + 1][n + 1];
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < n + 1; j++) {
arr[i][0] = 0;
arr[0][j] = 0;
}
}
for (int i = 1; i < n + 1; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for (int j = 1; j < n + 1; j++) {
arr[i][j] = Integer.parseInt((st.nextToken()));
}
}
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < n + 1; j++) {
arr[i][j] = arr[i][j] + arr[i][j - 1];
}
}
for (int i = 0; i < m; i++) {
StringTokenizer st2 = new StringTokenizer(br.readLine());
int x1 = Integer.parseInt(st2.nextToken());
int y1 = Integer.parseInt(st2.nextToken());
int x2 = Integer.parseInt(st2.nextToken());
int y2 = Integer.parseInt(st2.nextToken());
int sum1 = 0;
int sum2 = 0;
for (int j = x1; j < x2 + 1; j++) {
sum1 += arr[j][y1 - 1];
sum2 += arr[j][y2];
}
System.out.println(sum2 - sum1);
}
}
}
// 정답 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Baekjoon_11660 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] input = br.readLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
int[][] arr = new int[n + 1][n + 1];
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < n + 1; j++) {
arr[i][0] = 0;
arr[0][j] = 0;
}
}
for (int i = 1; i < n + 1; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for (int j = 1; j < n + 1; j++) {
arr[i][j] = Integer.parseInt((st.nextToken()));
}
}
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < n + 1; j++) {
arr[i][j] = arr[i][j] + arr[i][j - 1] + arr[i - 1][j] - arr[i - 1][j - 1];
}
}
for (int i = 0; i < m; i++) {
StringTokenizer st2 = new StringTokenizer(br.readLine());
int x1 = Integer.parseInt(st2.nextToken());
int y1 = Integer.parseInt(st2.nextToken());
int x2 = Integer.parseInt(st2.nextToken());
int y2 = Integer.parseInt(st2.nextToken());
int result = arr[x2][y2] - arr[x2][y1 - 1] - arr[x1 - 1][y2] + arr[x1 - 1][y1 - 1];
System.out.println(result);
}
}
}