알고리즘_PS/Baekjoon

[Baekjoon] 2042 - 구간 합 구하기 (C++)

hanseongjun 2022. 10. 2. 23:57
728x90
반응형

1. 문제

문제

어떤 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보다 작거나 같은 정수이다.

https://www.acmicpc.net/problem/2042

 

2042번: 구간 합 구하기

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 횟수이고, K는 구간의 합을 구하는 횟수이다. 그리고 둘째 줄부터 N+1번째 줄

www.acmicpc.net


2. 해결 아이디어 / 참고한 알고리즘

세그먼트 트리 알고리즘을 배우면서 푼 문제이다.

https://cocoon1787.tistory.com/313

 

[자료구조] 세그먼트 트리(구간트리, Segment Tree)로 구간 내 최소값 찾기

세그먼트 트리(Segment Tree)란? 알고리즘 문제를 풀다 보면 정렬되어 있지 않은 구간 내의 합이나 최솟값들을 빠르게 찾아야 하는 경우가 많습니다. 질문이 1개일 경우 간단하게 for문을 통해서 O(N)

cocoon1787.tistory.com

다음 블로그의 내용을 많이 참고했다. (정말 감사합니다 ㅜㅜ)

세그먼트 트리가 필요할 때는 다음과 같다.

배열에서 O(logn)의 속도로 어떤 구간의 정보를 얻어야 할 때
(-> 원래대로면 O(n)이 걸림)

구간합같은 경우, 이제까지의 합들을 누적해 풀이하는 DP식으로 (O(1)만에 구간합 나옴) 풀 수도 있겠지만,

중간에 구간의 값이 바뀌거나 하는 등의 작업이 일어날 경우, 구간의 합을 재계산할 때 결국 O(n)이 걸린다.

하지만, 이 경우에 세그먼트 트리를 사용한다면 이진트리에서 각 구간별로 합을 저장하므로 문제를 쉽게 해결할 수 있다.

 

그리고 꼭 구간합이 아니더라도, 세그먼트 트리를 이용해 구간 최솟값 등의 값을 빠르게 구할 수 있다.


3. 개선할 점

  • 처음 코드를 작성할 때 segTree의 인덱스를 1~n까지로 설정해 놓은 것을 잊어버리고 0~n-1까지로 생각해 코드를 짜버렸다.
  • segTree의 용량을 할당할 때, 보통 4n을 할당하는데 (정확히는 2^(ceil(log2(n))+1)), 실수로 2n이라고 생각했다.
  • query함수를 작성할 때, (..., st, mid, ..) + ..(.., mid+1, ed..) 라고 인자를 작성했어야 했는데, 헷갈렸다.
  • 배열에 직접 저장되는 값과 관련된 함수, 변수, 매개변수 등은 모두 long long int 형으로 작성해야 한다.
  • 무엇보다 여러 번 구현해서 익숙해져야겠다. DP가 한 유형으로만 나오지 않는 것처럼 세그먼트 트리도 구간합, 구간최솟값 등 여러 유형으로 등장할 수 있다. 골드1 이상 문제를 풀기 위해서는 무엇보다 세그먼트 트리를 잘 사용하는 것이 필수적이다.
  • int, long long int 형의 최댓값 크기만큼 비교해야 할 경우, climits 헤더를 include하는 것이 유용하다.

https://learn.microsoft.com/ko-kr/cpp/c-language/cpp-integer-limits?view=msvc-170 

 

C 및 C++ 정수 제한

자세한 정보: C 및 C++ 정수 제한

learn.microsoft.com


4. 코드

// Baekjoon No. 2042 구간 합 구하기 - 221003 solved
// Time Complexity O(nlogn)
// # Segment Tree

#include <iostream>
#define SIZE 1000000
using namespace std;

long long int segTree[SIZE * 4] = { 0 };
long long int numbers[SIZE] = { 0 };

long long int init(int nodeIdx, int st, int ed);
long long int query(int node, int st, int ed, int l, int r);
long long int modify(int node, int st, int ed, int b, long long int c);
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m, k;
    int i, a, b;
    long long int c;
    cin >> n >> m >> k;

    // numbers array input
    for (i = 0; i < n; i++)
        cin >> numbers[i];
    
    init(1, 1, n);  // init segment tree
    
    for (i = 0; i < m + k; i++) {
        cin >> a >> b >> c;
        // solving
        if (a == 1) {
            modify(1, 1, n, b, c);
        }
        else {
            cout << query(1, 1, n, b, c) << "\n";
        }
    }
    
    return 0;
}

long long int init(int nodeIdx, int st, int ed) {
    int mid = (st + ed) / 2;
    if (st == ed) 
        return segTree[nodeIdx] = numbers[st - 1];
    else 
        return segTree[nodeIdx] = init(nodeIdx * 2, st, mid) + init(nodeIdx * 2 + 1, mid + 1, ed);   
}

long long int query(int node, int st, int ed, int l, int r) {
    if (ed < l || r < st) 
        return 0;
    if (l <= st && ed <= r) 
        return segTree[node];
    int mid = (st + ed) / 2;
    return query(2 * node, st, mid, l, r) + query(2 * node + 1, mid + 1, ed, l, r);
}

long long int modify(int node, int st, int ed, int b, long long int c) {
    long long int tmp;
    if (st == ed) {
        tmp = segTree[node];
        segTree[node] = c;
    }
    else {
        int mid = (st + ed) / 2;
        tmp = (b > mid) ? modify(node * 2 + 1, mid + 1, ed, b, c) : modify(node * 2, st, mid, b, c);
        segTree[node] += c - tmp;
    }
    return tmp;
}
728x90
반응형
LIST