알고리즘, 문제해결/알고리즘 문제풀이
[백준 2042번] 구간 합 구하기
카펀
2021. 9. 25. 21:36
난이도: 골드 I
문제 링크: 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

이 문제는 세그먼트 트리라는 알고리즘을 알아야 풀 수 있는 문제입니다.
기본적인 설명은 여기를 참고해 주세요.
세그먼트 트리의 기본적인 문제입니다.
한 가지 유의할 점이 있다면, 값을 수정할 때입니다.
b의 값을 c만큼 수정하는 것이 아니라, b의 값을 c로 수정하는 것입니다.
따라서 차이값 difference = c - arr[b]가 되며, 차이값을 구할 때 arr을 참조하므로, arr[b]의 값 역시 수정하는 작업이 필요합니다.
이 외에는 세그먼트 트리 설명글과 거의 같으니 생략하겠습니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
#include <cmath> | |
using namespace std; | |
int n, m, k; | |
vector<long long> arr; | |
int closest_square (vector<long long> &arr) { | |
int a = (int)arr.size(); | |
int i = 1; | |
while(1) { | |
if (pow(i, 2) >= a) break; | |
i++; | |
} | |
return pow(i,2) * 4; | |
} | |
long long segment_tree(vector<long long> &tree, int start, int end, int node) { | |
if (start == end) return tree[node] = arr[start]; | |
int mid = (start + end) / 2; | |
return tree[node] = segment_tree(tree, start, mid, node*2) + segment_tree(tree, mid+1, end, node*2+1); | |
} | |
long long get_sum (vector<long long> &tree, int start, int end, int left, int right, int node) { | |
if (left > end || right < start) return 0; | |
if (left <= start && end <= right) return tree[node]; | |
int mid = (start + end) / 2; | |
return get_sum(tree, start, mid, left, right, node*2) + get_sum(tree, mid+1, end, left, right, node*2+1); | |
} | |
void edit_value(vector<long long> &tree, int start, int end, int node, int index, long long value) { | |
if (index < start || index > end) return; | |
tree[node] += value; | |
if (start == end) return; | |
int mid = (start + end) / 2; | |
edit_value(tree, start, mid, node*2, index, value); | |
edit_value(tree, mid+1, end, node*2+1, index, value); | |
} | |
int main() { | |
ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); | |
cin >> n >> m >> k; | |
arr.push_back(-1); | |
for (int i = 0; i < n; i++) { | |
int temp; | |
cin >> temp; | |
arr.push_back(temp); | |
} | |
int square = closest_square(arr); | |
vector<long long> tree(square); | |
segment_tree(tree, 1, n, 1); | |
for (int i = 0; i < m+k; i++) { | |
int a, b; | |
long long c; | |
cin >> a >> b >> c; | |
if (a == 1) { | |
//값 변경 | |
long long difference = c - arr[b]; | |
arr[b] = c; | |
edit_value(tree, 1, n, 1, b, difference); | |
} | |
else if (a == 2) { | |
//구간 b, c의 합 출력 | |
cout << get_sum(tree, 1, n, b, c, 1) << '\n'; | |
} | |
} | |
return 0; | |
} |
보통 이 문제는 채점 중 5% 지점에서 틀렸습니다 판정이 많이 나오는데,
앞서 말한 arr[b] 값 수정이 주된 원인이고, 또 하나는 tree의 크기를 너무 작게 잡으면 오답처리가 됩니다.

저도 위에서 언급한 두 부분에서 문제가 되어서 오답 판정을 여러 번 받았습니다 ㅠㅠ
세그먼트 트리의 가장 기초적인 문제라고 할 수 있는데 난이도가 골드 1이나 된다니... 실수할 여지가 많긴 하네요.