[백준] 2042번 구간 합 구하기 (C++)
문제
어떤 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번째 수를 c로 바꾸고 a가 2인 경우에는 b번째 수부터 c번째 수까지의 합을 구하여 출력하면 된다.
예제 입력 1
5 2 2
1
2
3
4
5
1 3 6
2 2 5
1 5 2
2 3 5
출력
첫째 줄부터 K줄에 걸쳐 구한 구간의 합을 출력한다.
단, 정답은 2^63-1보다 작거나 같다.
예제 출력 1
17
12
풀이
문제 해결 방법
이 문제는 세그먼트 트리를 사용하여 해결할 수 있습니다. 주요 해결 단계는 다음과 같습니다:
- 세그먼트 트리 구축: 주어진 수열로 세그먼트 트리를 만듭니다.
- 구간 합 쿼리: 세그먼트 트리를 이용하여 구간 합을 O(log N)에 계산합니다.
- 값 업데이트: 특정 위치의 값을 변경하고 세그먼트 트리를 업데이트합니다.
이 알고리즘의 시간 복잡도는 다음과 같습니다:
- 트리 구축: O(N)
- 구간 합 쿼리: O(log N)
- 값 업데이트: O(log N)
코드
#include <stdio.h>
int n, m, k;
long long idx[2100000];
long long data[1000001];
int leaf;
int main()
{
int i;
scanf("%d%d%d", &n, &m, &k);
for (leaf = 1; leaf < n; leaf *= 2);
for (i = 0; i < n; i++)
{
scanf("%lld", &data[i]);
idx[leaf + i] = data[i];
}
for (i = leaf - 1; i >= 1; i--)
{
idx[i] = idx[i * 2] + idx[i * 2 + 1];
}
int a, b;
long long c;
for (i = 1; i <= m + k; i++)
{
scanf("%d%d%lld", &a, &b, &c);
if (a == 1)
{
b += leaf - 1;
idx[b] = c;
b /= 2;
while (b >= 1)
{
idx[b] = idx[b * 2] + idx[b * 2 + 1];
b /= 2;
}
}
else
{
b += leaf - 1;
c += leaf - 1;
long long sum = 0;
while (b <= c)
{
if (b % 2 == 1) sum += idx[b++];
if (c % 2 == 0) sum += idx[c--];
b /= 2;
c /= 2;
}
printf("%lld\n", sum);
}
}
return 0;
}
댓글남기기