백준 2042 구간 합 구하기 C++

2023. 9. 16. 19:43알고리즘문제 풀이/백준

문제

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

 

풀이

단순 배열을 이용하면 시간복잡도가 O(N)이 되어 구간의 합을 느리는 속도가 너무 느리다.

이를 위해 다른 방법이 필요한데 세그먼트 트리(Segment Tree)를 사용하는 방법이 있다.

세그먼트 트리를 사용하면 시간복잡도가 O(logN)이 되어 합을 구하는 속도가 빨라진다.

 

init 함수: 구간 합 트리를 생성한다. 

update 함수: 특정 원소의 값을 수정한다.

sum 함수: 구간 합을 구한다.

 

코드

#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
long long init(vector<long long> &a, vector<long long> &tree, int node, int start, int end) {
	if (start == end) {
		return tree[node] = a[start];
	}
	else {
		return tree[node] = init(a, tree, node * 2, start, (start + end) / 2) + init(a, tree, node * 2 + 1, (start + end) / 2 + 1, end);
	}
}
void update(vector<long long> &tree, int node, int start, int end, int index, long long diff) {
	if (index < start || index > end) return;
	tree[node] = tree[node] + diff;
	if (start != end) {
		update(tree, node * 2, start, (start + end) / 2, index, diff);
		update(tree, node * 2 + 1, (start + end) / 2 + 1, end, index, diff);
	}
}
long long sum(vector<long long> &tree, int node, int start, int end, int left, int right) {
	if (left > end || right < start) {
		return 0;
	}
	if (left <= start && end <= right) {
		return tree[node];
	}
	return sum(tree, node * 2, start, (start + end) / 2, left, right) + sum(tree, node * 2 + 1, (start + end) / 2 + 1, end, left, right);
}
int main() {

	ios_base::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int n, m, k;
	cin >> n >> m >> k;
	vector<long long> a(n);
	int h = (int)ceil(log2(n));
	int tree_size = (1 << (h + 1));
	vector<long long> tree(tree_size);
	m += k;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	init(a, tree, 1, 0, n - 1);
	while (m--) {
		int t1;
		cin >> t1;
		if (t1 == 1) {
			int t2;
			long long t3;
			cin >> t2 >> t3;
			t2 -= 1;
			long long diff = t3 - a[t2];
			a[t2] = t3;
			update(tree, 1, 0, n - 1, t2, diff);
		}
		else if (t1 == 2) {
			int t2, t3;
			cin >> t2 >> t3;
			cout << sum(tree, 1, 0, n - 1, t2 - 1, t3 - 1) << '\n';
		}
	}
	return 0;
}

'알고리즘문제 풀이 > 백준' 카테고리의 다른 글

백준 2178 미로 탐색 C++  (0) 2023.09.16
백준 2164 카드2 C++  (0) 2023.09.16
백준 1978 소수 찾기 C++  (0) 2023.09.16
백준 1967 트리의 지름 C++  (0) 2023.09.16
백준 1931 회의실 배정 C++  (0) 2023.09.16