기술 블로그

세그먼트 트리(Segment Tree) 본문

알고리즘

세그먼트 트리(Segment Tree)

parkit 2019. 7. 29. 12:47
728x90
반응형

https://www.acmicpc.net/blog/view/9



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



세그먼트 트리(Segment Tree)





세그먼트 트리의 루트 노드는 0이 아닌 1부터 시작한다.(1, ...)

배열, vector의 인덱스 번호는 1이 아닌 0부터 시작한다.




함수의 인자들 중에서 node를 제외한 모든 것들은

 vector 또는 배열과 관련된 값들(인덱스, 값 자체 등)이라고 생각하면 된다.




초기 세그먼트 트리 설정하기

1
2
3
4
5
6
long long init_segment_tree(int node, int start, int end)
{
    if (start == endreturn tree[node] = v[start];
    return tree[node] = init_segment_tree(2 * node, start, (start + end/ 2)
        + init_segment_tree(2 * node + 1, (start + end/ 2 + 1end);
}
cs


왼쪽 자식 : 2 * node

오른쪽 자식 : (2 * node) + 1








합 구하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 합 구하기
long long sum(int node, int start, int endint left, int right)
{
    // [left, right]와 [start, end]가 겹치지 않는 경우
    if (left > end || right < start) return 0;
 
    // [left, right]가 [start, end]를 완전히 포함하는 경우
    if (left <= start && end <= right) return tree[node];
 
    // [start, end]가 [left, right]를 완전히 포함하는 경우와
    // [left, right]와 [start, end]가 겹쳐져 있는 경우(위의 3개를 제외 나머지 경우)
    return sum(2 * node, start, (start + end/ 2, left, right) 
        + sum(2 * node + 1, (start + end/ 2 + 1end, left, right);
}
cs









수 변경하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 수 변경하기(index번 째 수를 변경)
void update(int node, int start, int endint index, long long diff)
{
    // [start, end]에 index가 포함되지 않는 경우
    if (index < start || end < index) return;
 
    // [start, end]에 index가 포함되는 경우
    tree[node] += diff; // 직접적인 세그먼트 트리 내에 있는 배열의 수 변경
 
    // 리프 노드가 아닌 경우에는 자식도 변경해줘야 하기 때문에
    // start != end로 리프 노드인지 검사한다.(같으면 리프 노드임)
    if (start != end)
    {
        update(node, start, (start + end/ 2, index, diff);
        update(node, (start + end/ 2 + 1end, index, diff);
    }
}
cs










BOJ 2042번 구간 합 구하기


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



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <bits/stdc++.h>
 
using namespace std;
 
int N, M, K;
 
vector<long long> v, tree;
 
// 초기 세그먼트 트리 설정하기
long long init_segment_tree(int node, int start, int end)
{
    if (start == endreturn tree[node] = v[start];
 
    return tree[node] = init_segment_tree(2 * node, start, (start + end/ 2)
        + init_segment_tree(2 * node + 1, (start + end/ 2 + 1end);
}
 
// 합 구하기
long long sum(int node, int start, int endint left, int right)
{
    // [left, right]와 [start, end]가 겹치지 않는 경우
    if (left > end || right < start) return 0;
 
    // [left, right]가 [start, end]를 완전히 포함하는 경우
    if (left <= start && end <= right) return tree[node];
 
    // [start, end]가 [left, right]를 완전히 포함하는 경우와
    // [left, right]와 [start, end]가 겹쳐져 있는 경우(위의 3개를 제외 나머지 경우)
    return sum(2 * node, start, (start + end/ 2, left, right) 
        + sum(2 * node + 1, (start + end/ 2 + 1end, left, right);
}
 
// 수 변경하기(index번 째 수를 변경)
void update(int node, int start, int endint index, long long diff)
{
    // [start, end]에 index가 포함되지 않는 경우
    if (index < start || end < index) return;
 
    // [start, end]에 index가 포함되는 경우
    tree[node] += diff; // 직접적인 세그먼트 트리 내에 있는 배열의 수 변경
 
    // 리프 노드가 아닌 경우에는 자식도 변경해줘야 하기 때문에
    // start != end로 리프 노드인지 검사한다.(같으면 리프 노드임)
    if (start != end)
    {
        update(2 * node, start, (start + end/ 2, index, diff);
        update(2 * node + 1, (start + end/ 2 + 1end, index, diff);
    }
}
 
int main(void)
{
    int a;
    long long input;
 
    scanf("%d %d %d"&N, &M, &K);
 
    for (int i = 0; i < N; i++)
    {
        scanf("%lld"&input);
        v.push_back(input);
    }
    
    int h = (int)ceil(log2(N));
    int tree_size = 1 << (h + 1); // 세그먼트 트리 크기
 
    tree.assign(tree_size, 0); // 할당
 
    // 0하고 N - 1은 v vector에 대한 인덱스다.
    init_segment_tree(10, N - 1);
 
    for (int i = 0; i < M + K; i++)
    {
        scanf("%d"&a);
 
        // b번 째 수를 c로 변경
        if (a == 1)
        {
            int b;
            long long c;
 
            scanf("%d %lld"&b, &c);
 
            --b; // b는 vector에 대한 인덱스이므로 1 감소.
            
            long long diff = c - v[b];
 
            v[b] = c;
 
            update(10, N - 1, b, diff);
        }
        else if (a == 2// b번째 수 ~ c번째 수까지의 합
        {
            int b, c;
 
            scanf("%d %d"&b, &c);
 
            --b; --c; // b와 c는 vector에 대한 인덱스이므로 1씩 감소.
 
            // 1은 루트 노드 번호, 0과 N - 1 그리고 b와 c는 v vector에 대한 인덱스 번호
            printf("%lld\n", sum(10, N - 1, b, c));
        }
    }
 
    return 0;
}
cs























728x90
반응형

'알고리즘' 카테고리의 다른 글

해쉬 테이블(Hash Table)  (0) 2019.08.25
다익스트라(Dijkstra)  (0) 2019.08.17
Next Greater Element(NGE)  (0) 2019.07.06
2차원 배열을 함수 인자로 전달(포인터)  (0) 2019.07.01
SPFA (Shortest Path Faster Algorithm)  (0) 2019.06.01