17408번: 수열과 쿼리 24
길이가 N인 수열 A1, A2, ..., AN이 주어진다. 이때, 다음 쿼리를 수행하는 프로그램을 작성하시오 1 i v: Ai를 v로 바꾼다. (1 ≤ i ≤ N, 1 ≤ v ≤ 109) 2 l r: l ≤ i < j ≤ r을 만족하는 모든 Ai + Aj 중에서
www.acmicpc.net
그럴 수 밖에 없는 Idea
쿼리 : 100,000 <-- 대표적인 세그먼트트리 문제일 때의 쿼리 수
자주 바꿀 수 있다 <-- 세그먼트트리의 가능성
최댓값을 출력 <-- 세그먼트트리의 가능성
길이가 N인 수열 A1, A2, ..., AN이 주어진다. 이때, 다음 쿼리를 수행하는 프로그램을 작성하시오
수열의 인덱스는 1부터 시작한다. |
최솟값으로 떠올릴 수 있는 알고리즘
- 완전탐색
- 이분탐색
- 세그먼트트리
풀이
1. l ≤ i < j ≤ r을 만족하는 모든 Ai + Aj 중에서 최댓값
쉽게 생각하면 쉽고 어렵게 생각하면 어려워질 수 있다.
[l,r] 사이 두 수의 합의 최대값을 고른다고 할 때
먼저 떠오를 수 있는 것
1. 완전탐색 : $nC_2$
이어서 2차원 세그먼트트리를 구현해야하나 싶지만
2. 두 수의 합의 최대값 = 첫번째 최대값 + 두번째 최대값
첫번째 최대값 : 세그먼트트리 최대값
두번째 최대값 : 최대값을 제외한 양 옆구간
$2logN$ 으로 구할 수 있다.
시간복잡도 : $M*(2logN)$
쿼리 : $M$
두 수의 합의 최대값 : 첫번째 최대값 + 두번째 최대값 = $2logN$
update : $logN$
#include<stdio.h> #define Max 100000 typedef struct node { int idx; int num; }Node; int SHIFT = 1; Node segTree[Max * 4]; int N, M; void makeSHIFT() { while (SHIFT < N) SHIFT <<= 1; SHIFT -= 1; } void update(int dataIdx, int diff) { int nodeIdx = dataIdx + SHIFT; //주의1.leaf 에는 값을 직접 넣음 segTree[nodeIdx].num = diff; segTree[nodeIdx].idx = dataIdx; while (nodeIdx > 1) { //주의2.nodeIdx는 parent 역할 nodeIdx >>= 1; int childLeft = nodeIdx * 2; int childRight = nodeIdx * 2 + 1; segTree[nodeIdx] = segTree[childLeft].num > segTree[childRight].num ? segTree[childLeft] : segTree[childRight]; } } Node query(int dataLeft, int dataRight) { int nodeLeft = dataLeft + SHIFT; int nodeRight = dataRight + SHIFT; Node max = { 0,0 }; while (nodeLeft < nodeRight) { if (nodeLeft % 2) { max = max.num > segTree[nodeLeft].num ? max : segTree[nodeLeft]; nodeLeft += 1; nodeLeft >>= 1; } else nodeLeft >>= 1; if (nodeRight % 2) nodeRight >>= 1; else { max = max.num > segTree[nodeRight].num ? max : segTree[nodeRight]; nodeRight -= 1; nodeRight >>= 1; } } if (nodeLeft == nodeRight) max = max.num > segTree[nodeLeft].num ? max : segTree[nodeLeft]; return max; } int main() { scanf("%d", &N); makeSHIFT(); for (int i = 1; i <= N; i++) { int n; scanf("%d", &n); update(i, n); } scanf("%d", &M); while (M--) { int a, b, c; scanf("%d %d %d", &a, &b, &c); if (a == 1) update(b, c); else { Node max1 = query(b, c); Node max2_1 = query(b, max1.idx - 1); Node max2_2 = query(max1.idx + 1, c); int res = (max1.num + max2_1.num) > (max1.num + max2_2.num) ? (max1.num + max2_1.num) : (max1.num + max2_2.num); printf("%d\n", res); } } return 0; }
주의
1. 세그먼트트리의 최소값 최대값 update 시 :
- leaf에는 값을 직접 넣는다.
- update 시 nodeIdx 는 parent로써 역할을 하므로 반복 조건은 nodeIdx > 1 이다.
- 최소값을 구할경우 전체 값에 대해 Max 업데이트를 해야한다.
'Algorithm_library > 트리' 카테고리의 다른 글
세그먼트트리 (0) | 2021.03.21 |
---|