알고리즘 스터디 - 3주차
'22. 2. 11. 알고리즘 스터디 내용 및 회고
주제
- Segment Tree(lazy propagation)
- 백준 10999번(lazy propagation을 이용한 문제)
Segment Tree(lazy propagation)
Segment Tree에서 구간 업데이트를 빠르게 하기 위해 적용한 lazy propagation에 대해 공부하였음.
컴퓨터구조에서 배운 캐시의 개념과 비슷하게 생각하였음.
참고 : https://bowbowbow.tistory.com/41. 문제점
구간에 대해 업데이트를 한꺼번에 실행할 때, 기존 segment tree를 이용할 경우 모든 node를 거쳐가며 업데이트를 해줘야한다. 이때 한 개의 업데이트는 ()이 걸리는데, 여기서 m의 길이의 구간을 업데이트 한다고 할 때 ()가 된다. 시간이 오래 걸리므로 해결 방안이 필요함.
2. 아이디어
구간에 대해 업데이트 시 모든 노드에 같은 수를 빼고, 더할 경우 해당 노드에 자식 노드의 갯수 * 바뀌는 값만큼 적용해주면 똑같다.
해당 아이디어에서 문제점은 해당 구간에서의 구간합은 적용이 되지만 자식노드에 적용이 안되는 것이다. 그렇다고 자식노드를 또 업데이트 할 경우, 기존 segment tree와 별반 차이가 없다.
따라서 lazy라는 별도의 배열을 사용하여, 얼마만큼 변화를 적용시키는 지를 적용한 후, 해당 노드가 필요할 경우 lazy만큼 노드에 적용시켜 값을 얻는다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static int stoi(String s) {return Integer.parseInt(s);}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = stoi(br.readLine());
int[] arr = new int[n];
StringTokenizer st = new StringTokenizer(br.readLine());
for(int i = 0; i < arr.length; i++) {
arr[i] = stoi(st.nextToken());
}
Tree tree = new Tree(n, arr);
// update -> start = 0, end = n - 1, left ~ right = to change dif;
tree.update(0, n - 1, 1, 2, 4, -1);
// sum -> start = 0, end = n - 1, left ~ right = sum left ~ right;
System.out.println(tree.sum(0, n - 1, 1, 4, 4));
}
public static class Tree {
int[] arr, tree, lazy;
public Tree(int n, int[] arr) {
this.arr = new int[n];
tree = new int[n * 4];
lazy = new int[n * 4];
for(int i = 0; i < arr.length; i++) {
this.arr[i] = arr[i];
}
init(0, n - 1, 1);
}
public int init(int start, int end, int node) {
if(start == end) return tree[node] = arr[start];
int mid = (start + end) / 2;
return tree[node] = init(start, mid, node * 2) + init(mid + 1, end, node * 2 + 1);
}
public void propagate(int start, int end, int node) {
if(lazy[node] != 0) {
if(start != end) {
lazy[node * 2] += lazy[node];
lazy[node * 2 + 1] += lazy[node];
}
tree[node] += lazy[node] * (end - start + 1);
lazy[node] = 0;
}
}
public int sum(int start, int end, int node, int left, int right) {
propagate(start, end, node);
if(left > end || right < start) {
return 0;
}
if(left <= start && end <= right) {
return tree[node];
}
int mid = (start + end) / 2;
return sum(start, mid, node * 2, left, right) + sum(mid + 1, end, node * 2 + 1, left, right);
}
public void update(int start, int end, int node, int left, int right, int dif) {
propagate(start, end, node);
if(right < start || left > end) return;
if(left <= start && end <= right) {
lazy[node] = dif;
propagate(start, end, node);
return;
}
int mid = (start + end) / 2;
update(start, mid, node * 2, left, right, dif);
update(mid + 1, end, node * 2 + 1, left, right, dif);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
public void print() {
for(int i = 0; i < tree.length; i++) {
System.out.println(i + " : " + tree[i]);
}
}
}
}
백준 10999번(lazy propagation을 이용한 문제) : https://www.acmicpc.net/problem/10999
기존 2042번 구간 합 구하기 문제에서 사용했던 segment tree를 이용할 경우 시간초과가 나는 문제임.
다중 노드에 같은 수를 더하거나 빼는 문제로 lazy propagation를 적용한 segment tree를 사용하여 문제를 풀어야 한다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static int stoi(String s) {return Integer.parseInt(s);}
public static long stol(String s) {return Long.parseLong(s);}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = stoi(st.nextToken());
int M = stoi(st.nextToken());
int K = stoi(st.nextToken());
long[] arr = new long[N];
for(int i = 0; i < arr.length; i++) {
arr[i] = stol(br.readLine());
}
Tree tree = new Tree(N, arr);
for(int i = 0; i < M + K; i++) {
st = new StringTokenizer(br.readLine());
int a = stoi(st.nextToken());
int b = stoi(st.nextToken()) - 1;
int c = stoi(st.nextToken()) - 1;
if(a == 1) {
tree.update(0, N - 1, 1, b, c, stol(st.nextToken()));
}
else {
System.out.println(tree.sum(0, N - 1, 1, b, c));
}
}
}
public static class Tree {
long[] arr, tree, lazy;
public Tree(int n, long[] arr) {
this.arr = new long[n];
tree = new long[n * 4];
lazy = new long[n * 4];
for(int i = 0; i < arr.length; i++) {
this.arr[i] = arr[i];
}
init(0, n - 1, 1);
}
public long init(int start, int end, int node) {
if(start == end) return tree[node] = arr[start];
int mid = (start + end) / 2;
return tree[node] = init(start, mid, node * 2) + init(mid + 1, end, node * 2 + 1);
}
public void propagate(int start, int end, int node) {
if(lazy[node] != 0) {
if(start != end) {
lazy[node * 2] += lazy[node];
lazy[node * 2 + 1] += lazy[node];
}
tree[node] += lazy[node] * (end - start + 1);
lazy[node] = 0;
}
}
public long sum(int start, int end, int node, int left, int right) {
propagate(start, end, node);
if(left > end || right < start) {
return 0;
}
if(left <= start && end <= right) {
return tree[node];
}
int mid = (start + end) / 2;
return sum(start, mid, node * 2, left, right) + sum(mid + 1, end, node * 2 + 1, left, right);
}
public void update(int start, int end, int node, int left, int right, long dif) {
propagate(start, end, node);
if(right < start || left > end) return;
if(left <= start && end <= right) {
lazy[node] = dif;
propagate(start, end, node);
return;
}
int mid = (start + end) / 2;
update(start, mid, node * 2, left, right, dif);
update(mid + 1, end, node * 2 + 1, left, right, dif);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
public void print() {
for(int i = 0; i < tree.length; i++) {
System.out.println(i + " : " + tree[i]);
}
}
}
}
Author And Source
이 문제에 관하여(알고리즘 스터디 - 3주차), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@qudgnl0422/알고리즘-스터디-3주차저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)