2 분 소요

문제

어떤 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

풀이

문제 해결 방법

이 문제는 세그먼트 트리를 사용하여 해결할 수 있습니다. 주요 해결 단계는 다음과 같습니다:

  1. 세그먼트 트리 구축: 주어진 수열로 세그먼트 트리를 만듭니다.
  2. 구간 합 쿼리: 세그먼트 트리를 이용하여 구간 합을 O(log N)에 계산합니다.
  3. 값 업데이트: 특정 위치의 값을 변경하고 세그먼트 트리를 업데이트합니다.

이 알고리즘의 시간 복잡도는 다음과 같습니다:

  • 트리 구축: 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;
}

댓글남기기