반응형
리뷰
세그먼트 트리를 통해 수열의 특정 구간의 첫번째 최대값과 두번째 최대값을 구하고 그 합을 구해 출력하는 문제
https://www.acmicpc.net/problem/17408
문제 풀이
전역 변수
MAX_N : 노드의 최대 개수 10만을 상수형 정수타입으로 설정해 준다.
nodes : 수열의 정보를 담을 배열, 크기는 MAX_N으로 설정한다.
tree : 최대값과 인덱스를 담을 정수형 pair 배열, 크기는 MAX_N * 4로 설정한다.
n, m : 노드의 개수 및 쿼리의 개수 정보를 담을 변수
함수
1. 최대값을 갖는 세그먼트 트리를 만드는 함수
void build(int node, int start, int end)
- 리프 노드 도달 시 배열의 값과 인덱스를 저장해 준다.
- 이후 탐색을 통해 위로 올려주며 각 자식노드의 최대값을 상위 노드의 최대값과 인덱스로 갱신시켜 준다.
2. 구간의 최대값을 업데이트 해주는 함수
void update(int node, int start, int end, int idx, int val)
- 리프 노드 도달 시 val과 idx를 업데이트 해준다.
- 이후 탐색을 통해 위로 올려주며 각 자식노드의 최대값을 상위 노드의 최대값과 인덱스로 갱신시켜 준다.
3. 구간의 최대값과 인덱스를 구하는 함수
pair<int, int> query(int node, int start, int end, int L, int R)
- L과 R 둘중 하나라도 start와 end의 범위를 벗어난 경우 문제 내 최소값인0과 범위를 벗어난 임의의 인덱스 출력
- 구간 내의 경우 tree의 node번째 데이터를 리턴해 준다.
- 더 구할 범위의 경우 이분 탐색과 재귀를 통해 left, right 정보를 받아온다.
- left와 right의 값 비교를 통해 더 큰값을 리턴해 준다.
문제풀이
- n값을 입력 받고, nodes 배열에 수열의 정보를 입력 받아준다.
- build 함수를 통해 nodes 배열을 사용하여 세그먼트 트리를 만들어 준다.
- m값을 입력 받고 m개의 쿼리를 실행해 준다, a, b, c를 통해 각 수를 입력받는다.
- a가 1일 경우 update 함수를 통해 b - 1, c - 1구간을 업데이트 해준다. (0 based-index 형식이므로 1을 빼준 것)
- b가 1일 경우 query 함수를 통해 b - 1, c - 1구간 중 최대값과 인덱스를 받아온다.
- 업데이트 함수를 통해 해당 인덱스의 값을 0으로 업데이트 해준다.
- 해당 구간의 최대값을 하나 더 뽑아온다.
- 업데이트를 통해 이전에 6번에서 0으로 업데이트한 인덱스를 5에서 뽑아온 최대값으로 다시 업데이트 한다.
- 두 최대값의 합을 출력해 준다.
참고 사항
2번 명령이 주어졌을 경우 O(4 * LogN) 크기로 시간상 괜찮아 보여 돌렸더니 꽤 빠른 속도로 동작 했다.
기존엔 벡터를 사용 했었는데 배열을 사용하니 더 빠른 속도로 동작이 가능한 것 같다.
정답 코드
#include<iostream>
using namespace std;
const int MAX_N = 100000;
int nodes[MAX_N];
pair<int, int> tree[MAX_N * 4];
int n, m;
void build(int node, int start, int end) {
if (start == end) tree[node] = { nodes[start], start };
else {
int mid = (start + end) / 2;
build(node * 2, start, mid);
build(node * 2 + 1, mid + 1, end);
if (tree[node * 2].first >= tree[node * 2 + 1].first) tree[node] = tree[node * 2];
else tree[node] = tree[node * 2 + 1];
}
}
void update(int node, int start, int end, int idx, int val) {
if (start == end) tree[node] = { val, idx };
else {
int mid = (start + end) / 2;
if (start <= idx && idx <= mid) update(node * 2, start, mid, idx, val);
else update(node * 2 + 1, mid + 1, end, idx, val);
if (tree[node * 2].first >= tree[node * 2 + 1].first) tree[node] = tree[node * 2];
else tree[node] = tree[node * 2 + 1];
}
}
pair<int, int> query(int node, int start, int end, int L, int R) {
if (R < start || end < L) return { 0, -1 };
if (L <= start && end <= R) return tree[node];
int mid = (start + end) / 2;
pair<int, int> left = query(node * 2, start, mid, L, R);
pair<int, int> right = query(node * 2 + 1, mid + 1, end, L, R);
if (left.first >= right.first) return left;
return right;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 0; i < n; i++) cin >> nodes[i];
build(1, 0, n - 1);
cin >> m;
while (m--) {
int a, b, c; cin >> a >> b >> c;
if (a == 1) update(1, 0, n - 1, b - 1, c);
else {
pair<int, int> max1 = query(1, 0, n - 1, b - 1, c - 1);
update(1, 0, n - 1, max1.second, 0);
pair<int, int> max2 = query(1, 0, n - 1, b - 1, c - 1);
update(1, 0, n - 1, max1.second, max1.first);
cout << max1.first + max2.first << "\n";
}
}
}
728x90
반응형
'알고리즘 공부 > C++' 카테고리의 다른 글
[P4] 백준 16975번 수열과 쿼리 21 C++ 세그먼트 트리, 느리게 갱신되는 세그먼트 트리 (2) | 2024.09.22 |
---|---|
[P4] 백준 10999번 구간 합 구하기 2 C++ 세그먼트 트리, 느리게 갱신되는 세그먼트 트리 (0) | 2024.09.22 |
[G1] 백준 12100번 2048 (Easy) C++ 백트래킹, 구현, 시뮬레이션, 브루트포스 알고리즘 (1) | 2024.09.21 |
[G5] 백준 15681번 트리와 쿼리 C++ DFS, 깊이 우선 탐색, 트리 (1) | 2024.09.20 |
[G4] 백준 2239번 스도쿠 C++ 백트래킹, 구현 (0) | 2024.09.20 |