고코딩
[백준 2042번] 구간 합 구하기 본문
문제
어떤 N개의 수가 주어져 있다. 그런데 중간에 수의 변경이 빈번히 일어나고 그 중간에 어떤 부분의 합을 구하려 한다. 만약에 1,2,3,4,5 라는 수가 있고, 3번째 수를 6으로 바꾸고 2번째부터 5번째까지 합을 구하라고 한다면 17을 출력하면 되는 것이다. 그리고 그 상태에서 다섯 번째 수를 2로 바꾸고 3번째부터 5번째까지 합을 구하라고 한다면 12가 될 것이다.
입력
첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 횟수이고, K는 구간의 합을 구하는 횟수이다. 그리고 둘째 줄부터 N+1번째 줄까지 N개의 수가 주어진다. 그리고 N+2번째 줄부터 N+M+K+1번째 줄까지 세 개의 정수 a, b, c가 주어지는데, a가 1인 경우 b(1 ≤ b ≤ N)번째 수를 c로 바꾸고 a가 2인 경우에는 b(1 ≤ b ≤ N)번째 수부터 c(b ≤ c ≤ N)번째 수까지의 합을 구하여 출력하면 된다.
입력으로 주어지는 모든 수는 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.
출력
첫째 줄부터 K줄에 걸쳐 구한 구간의 합을 출력한다. 단, 정답은 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.
세그먼트 트리의 개념을 이해하는게 정말 어려웠다. 그림도 그리고 이해는 안되지만 코드도 직접 입력해보고 많이 힘들었다.
https://blog.naver.com/ndb796/221282210534
위의 블로그의 글을 참고해서 세그먼트를 이해했다.
이 문제의 가장 중요 포인트는 3가지 인것 같다. 숫자를 long타입으로 저장, 수정되는 값, 세그먼트에서 리프노드에서 수정된 값을 배열에서도 수정
수정되는 값 이라는 뜻은 기존의 세그먼트 트리에서 리프노드의 값은 10 -> 20 으로 바뀌는 형식이 아니라 10이 +10만큼 값이 추가된다 라고 예제를 많이 든다. 그 부분이 세그먼트 트리를 처음 배울때 헷갈릴수 있다.
나중에 세그먼트 트리 개념에 대해서도 따로 포스팅으로 해야겠다.
정답
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
public class Main {
static long[] arr; //주어진 N개의 수를 담을 배열
static long[] tree; //배열의 이용해 만들 구간의 합 트리
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String[] NMK = br.readLine().split(" ");
int N = Integer.parseInt(NMK[0]);
int M = Integer.parseInt(NMK[1]);
int K = Integer.parseInt(NMK[2]);
arr = new long[N];
tree = new long[N*4]; //세그먼트르리 최대 크기 잡아놓음
for(int i=0;i<N;i++) {
arr[i] = Integer.parseInt(br.readLine());
}
//세그먼트 트리 생성
init(0, arr.length-1 , 1);
for(int i=0;i< M+K ;i++) {
long sum=0;
String [] command = br.readLine().split(" ");
int a = Integer.parseInt(command[0]);
int b = Integer.parseInt(command[1]);
long c = Integer.parseInt(command[2]);
if(a == 1) {//update b: 수정할 배열의 위치 c:수정할 값
long val = c-arr[b-1];
arr[b-1] += val; //세그먼트 트리 배열 뿐만 아니라 기존 배열의 값도 변경
update(0,arr.length-1,b-1,(int)val,1);
}else { //sum 인덱스 B부터 C까지의 값의 합을 구한다
sum=sum(0,arr.length-1,b-1,(int)c-1,1);
System.out.println(sum);
}
}
br.close();
bw.flush();
bw.close();
}
public static long sum(int start, int end, int left, int right, int node) {
//범위를 벗어나면
if(end<left || start > right)
return 0;
//범위가 겹치면
if(left<=start && end<=right)
return tree[node];
//범위가 걸치면 중간을 나눠서 다시 계산
int mid = (start+end)/2;
return sum(start,mid,left,right,node*2) + sum(mid+1,end,left,right,node*2+1);
}
public static void update(int start, int end, int index, int val, int node) {
//지금 보고있는 인덱스의 범위에 내가 변경하려는 인덱스가 없으면 이곳을 변경사항이 없음
if(start > index || index > end)
return;
tree[node] += val;
if(start == end)
return;
int mid = (start+end)/2;
update(start,mid,index,val,node*2);
update(mid+1,end,index,val,node*2+1);
}
//arr배열의 인덱스 start부터 end까지 세그먼트 트리로 만든다 루트의 인덱스는 node이다.
// start~end까지의 합을 리턴해주는 함수
public static 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);
}
}
'코딩테스트' 카테고리의 다른 글
[백준 1021번] 회전하는 큐 (0) | 2021.01.07 |
---|---|
[백준 1717번] 집합의 표현 (0) | 2021.01.07 |
[백준 1927번] 최소 힙 (0) | 2021.01.05 |
[백준 1406번] 에디터 (0) | 2021.01.05 |
[백준 3190번] 뱀 (0) | 2021.01.05 |