고코딩

[백준 2042번] 구간 합 구하기 본문

코딩테스트

[백준 2042번] 구간 합 구하기

고코딩 2021. 1. 6. 16:44

문제

어떤 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

 

41. 세그먼트 트리(Segment Tree)

이번 시간에 다룰 내용은 여러 개의 데이터가 연속적으로 존재할 때 특정한 범위의 데이터의 합을 구하는 ...

blog.naver.com

https://www.crocus.co.kr/648

 

세그먼트 트리(Segment Tree)

세그먼트 트리(Segment Tree)는 요청하는 쿼리에 대해 방식이 달라질 수 있으나, 모든 쿼리를 다룰 수 없기에 구간 합에 대한 세그먼트 트리를 정리해 두었습니다. 내용이 길지만 그만큼 자세히 설

www.crocus.co.kr

위의 블로그의 글을 참고해서 세그먼트를 이해했다.

이 문제의 가장 중요 포인트는 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