펜윅트리는 비트연산을 통해 빠른 시간에 구간합을 구하는 배열 형태의 트리이다.
이 글에서는 2차원 펜윅트리만을 설명하고 있다.
1차원 펜윅트리에 대한 이해가 필요하면 블로그 내 펜윅트리 글(https://uldada.tistory.com/5)을 참고하자.
2차원 펜윅트리
우선 2차원 펜윅트리의 Update와 Sum 함수를 보자.
코드를 보면 x축(여기에선 열을 의미)으로 펜윅트리가 동작하며, 그 안에 while문을 통해 y축(행을 의미)으로 펜윅트리가 동작한다.
일단 update를 통해 초기화(입력받은 배열을 통해 펜윅트리 채워나가기)를 진행해 보자 초기화는 update 함수를 통해 진행된다.(1차원 펜윅트리와 동일한 방식)
입력받은 배열은 아래와 같다.
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
첫번째 행부터 채워보자.
다음, 두번째 행을 채워보자.
다음, 세번째, 네번째 행을 바로 채워보자.
이를 확인해 보면, 오른쪽으로 펜윅트리가 동작함과 동시에, 아래쪽으로도 펜윅트리가 동작한다.
좀 더 구체적으로 설명하면, 아래 그림처럼 오른쪽으로 채워지는 펜윅트리의 각각의 열이 아래쪽으로 채워지고 있다.
쉽게 말해 while루프 중첩을 통해 오른쪽으로 더해지며, 이 값들이 아래쪽으로 더해지는 것이다.
따라서, sum 함수 또한 위 코드와 같이 while루프 중첩을 통해 합을 구할 수 있는 것이다.
이를 통해 2차원 배열의 구간합을 구할 수 있는데, 방법은 간단하다.
아래와 같이 2차원 배열에서 (x1,y1) ~ (x2,y2) 구간합을 구하고자 한다.
이는 펜윅트리에서 아래와 같이 sum함수를 계산함으로써 그 값을 구할 수 있게 된다.
(마찬가지로 x축은 세로, y축은 가로로 설정하였다.)
(x1,y1)~(x2,y2)의 구간합 : sum(x2,y2) – sum(x1-1,y2) – sum(x2,y1-1) + sum(x1-1,y1-1)
지금까지 2차원 펜윅트리의 동작방식에 대해 구체적으로 살펴 보았다.
이를 이해한다면 3차원, 4차원 등 고차원 펜윅트리를 만드는 것 또한 쉽게 이해할 수 있을 듯 하다.
백준 11658번 구간 합 구하기 3번을 아래와 같이 풀이하였다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
public static int[][] matrix;
public static int[][] tree;
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine().trim());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
matrix = new int[n+1][n+1];
tree = new int[n+1][n+1];
// Initialize Tree and Matrix
for(int i=1;i<=n;i++) {
StringTokenizer st2 = new StringTokenizer(br.readLine().trim());
int cnt = st2.countTokens();
for(int j=1;j<=cnt;j++) {
int val = Integer.parseInt(st2.nextToken());
matrix[i][j] = val;
update(tree, i, j, val);
}
}
// Quary solve
for(int i=0;i<m;i++) {
StringTokenizer st3 = new StringTokenizer(br.readLine().trim());
int w = Integer.parseInt(st3.nextToken());
if(w == 0) {
int x = Integer.parseInt(st3.nextToken());
int y = Integer.parseInt(st3.nextToken());
int c = Integer.parseInt(st3.nextToken());
int _diff = c-matrix[x][y];
matrix[x][y] = c;
update(tree, x, y, _diff);
}
else if(w == 1) {
int x1 = Integer.parseInt(st3.nextToken());
int y1 = Integer.parseInt(st3.nextToken());
int x2 = Integer.parseInt(st3.nextToken());
int y2 = Integer.parseInt(st3.nextToken());
int ans = sum(tree, x2, y2) - sum(tree, x1-1, y2) - sum(tree, x2, y1-1) + sum(tree, x1-1, y1-1);
System.out.println(ans);
}
}
}
public static void update(int[][] tree, int idx_x, int idx_y, int diff) {
while(idx_x<tree.length) {
int temp_idx_y = idx_y;
while(temp_idx_y<tree[idx_x].length) {
tree[idx_x][temp_idx_y] += diff;
temp_idx_y += (temp_idx_y&-temp_idx_y);
}
idx_x += (idx_x&-idx_x);
}
}
public static int sum(int[][] tree, int idx_x, int idx_y) {
int sol = 0;
while(idx_x>0) {
int temp_idx_y = idx_y;
while(temp_idx_y>0) {
sol += tree[idx_x][temp_idx_y];
temp_idx_y -= (temp_idx_y&-temp_idx_y);
}
idx_x -= (idx_x&-idx_x);
}
return sol;
}
}
'알고리즘 > 개념' 카테고리의 다른 글
펜윅 트리(1차원 펜윅 트리, Fenwick Tree, Binary Indexed Tree) (0) | 2020.03.16 |
---|