본문 바로가기

Algorithm

백준 10999번 구간 합 구하기 2

반응형

문제

어떤 N개의 수가 주어져 있다. 그런데 중간에 수의 변경이 빈번히 일어나고 그 중간에 어떤 부분의 합을 구하려 한다. 만약에 1,2,3,4,5 라는 수가 있고, 3번째부터 4번째 수에 6을 더하면 1, 2, 9, 10, 5가 되고, 여기서 2번째부터 5번째까지 합을 구하라고 한다면 26을 출력하면 되는 것이다. 그리고 그 상태에서 1번째부터 3번째 수에 2를 빼고 2번째부터 5번째까지 합을 구하라고 한다면 22가 될 것이다.

입력

첫째 줄에 수의 개수 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, b, c, d가 주어지는데, a가 1인 경우 b번째 수부터 c번째 수에 d를 더하고, a가 2인 경우에는 b부터 c까지의 합을 구하여 출력하면 된다.

입력으로 주어지는 모든 수는 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.

출력

첫째 줄부터 K줄에 걸쳐 구한 구간의 합을 출력한다. 단, 정답은 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.

 

www.acmicpc.net/blog/view/26

 

세그먼트 트리 나중에 업데이트 해야지!

배열 A가 있고, 여기서 다음과 같은 두 연산을 수행해야하는 문제가 있습니다. 10999번 문제: 구간 합 구하기 2 구간 l, r (l ≤ r)이 주어졌을 때, A[l] + A[l+1] + ... + A[r-1] + A[r]을 구해서 출력하기 i번째

www.acmicpc.net

 

하나의 인덱스를 수정하는 것이 아니라 범위를 통해 갱신하는 것은 update를 수정하면 됨.

재귀를 활용한 DP식으로 가능한데 

void update(int n, int s,int e, int b, int c , int d)
{
	if(c < s || e < b)
	{
		return;
	}

	if (s == e)
	{
		tree[n] += d;
		return;
	}
	int m = (s + e) / 2;
	 update(2 * n, s, m, b, c, d); 
	 update(2 * n + 1, m + 1, e, b, c, d);
	 tree[n] = tree[n * 2] + tree[n * 2 + 1];
}

리프까지 내려가서 포함되는 노드에 값 더해주고 리프가 아닌 그 윗단계 노드들은 리프부터 더해오면서 구해줌.

=> 이거는 단일 인덱스에서도 적용가능

하지만 이번 문제에서는 시간초과가 발생한다.

 

따라서 업데이트를 매번 수행하지 말고, lazy 업데이트 방식을 사용해야 하는데, 사실 실전에서 바로 적용하긴 힘든 것 같아 코드를 이해하는 수준으로 공부하려고 한다.

 

 

#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
// a: 배열 a
// tree: 세그먼트 트리
// node: 세그먼트 트리 노드 번호
// node가 담당하는 합의 범위가 start ~ end
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_lazy(vector<long long> &tree, vector<long long> &lazy, int node, int start, int end) {
    if (lazy[node] != 0) {
        tree[node] += (end-start+1)*lazy[node];
        // leaf가 아니면
        if (start != end) {
            lazy[node*2] += lazy[node];
            lazy[node*2+1] += lazy[node];
        }
        lazy[node] = 0;
    }
}
void update_range(vector<long long> &tree, vector<long long> &lazy, int node, int start, int end, int left, int right, long long diff) {
    update_lazy(tree, lazy, node, start, end);
    if (left > end || right < start) {
        return;
    }
    if (left <= start && end <= right) {
        tree[node] += (end-start+1)*diff;
        if (start != end) {
            lazy[node*2] += diff;
            lazy[node*2+1] += diff;
        }
        return;
    }
    update_range(tree, lazy, node*2, start, (start+end)/2, left, right, diff);
    update_range(tree, lazy, node*2+1, (start+end)/2+1, end, left, right, diff);
    tree[node] = tree[node*2] + tree[node*2+1];
}
long long sum(vector<long long> &tree, vector<long long> &lazy, int node, int start, int end, int left, int right) {
    update_lazy(tree, lazy, node, start, end);
    if (left > end || right < start) {
        return 0;
    }
    if (left <= start && end <= right) {
        return tree[node];
    }
    return sum(tree, lazy, node*2, start, (start+end)/2, left, right) + sum(tree, lazy, node*2+1, (start+end)/2+1, end, left, right);
}
int main() {
    int n, m, k;
    scanf("%d %d %d",&n,&m,&k);
    vector<long long> a(n);
    int h = (int)ceil(log2(n));
    int tree_size = (1 << (h+1)) - 1;
    vector<long long> tree(tree_size);
    vector<long long> lazy(tree_size);
    m += k;
    for (int i=0; i<n; i++) {
        scanf("%lld",&a[i]);
    }
    init(a, tree, 1, 0, n-1);
    while (m--) {
        int t1,t2,t3;
        scanf("%d",&t1);
        if (t1 == 1) {
            int start, end;
            long long v;
            scanf("%d %d %lld",&start,&end, &v);
            update_range(tree, lazy, 1, 0, n-1, start-1, end-1, v);
        } else if (t1 == 2) {
            int start, end;
            scanf("%d %d",&start,&end);
            printf("%lld\n",sum(tree, lazy, 1, 0, n-1, start-1, end-1));
        }
    }
    return 0;
}

 

2~8의 범위에서 수정이 일어난 경우

2~8을 모두 포함하는 2~9 를 담당하는 노드에서는 해당 노드를 루트로 하는 서브트리는 모두 수정이 일어나야한다.

따라서 모든 범위를 포함하는 서브트리의 루트는 바로 업데이트 하고 그 서브트리 노드는 업데이트 해야하는 값만 저장해 둔 뒤 아래로 내려간다.

만약 자신이 위로부터 받은 업데이트 값 (lazy 값)이 있다면 그것을 바탕으로 자신을 갱신하고, 자신이 모든 범위를 포함하는 노드가 아니라면 밑으로 내려간다.

반응형

'Algorithm' 카테고리의 다른 글

백준 11497번 통나무 건너뛰기  (0) 2020.09.03
백준 11000번 강의실 배정  (0) 2020.09.03
백준 15558번 점프 게임  (0) 2020.09.02
백준 2042번 구간 합 구하기  (0) 2020.09.02
백준 15653번 구슬 탈출 4  (0) 2020.09.01