문제
https://www.acmicpc.net/problem/14428
풀이(25분)
import java.io.*;
import java.util.*;
public class Main {
static int squareNum;
static Node[] segmentTree;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
int n = Integer.parseInt(br.readLine());
int square = 0;
while(Math.pow(2, square) < n){ // 배열 크기를 위한 지수 찾기
square++;
}
squareNum = (int)Math.pow(2, square); // 인덱스 보정값
segmentTree = new Node[2 * squareNum]; // 2^(k+1) 로 배열 선언
st = new StringTokenizer(br.readLine());
for(int i = 1; i <= n; i++){
int value = Integer.parseInt(st.nextToken());
segmentTree[squareNum + i - 1] = new Node(value, i); // 배열 입력
}
for(int i = squareNum + n; i < 2 * squareNum; i++){
segmentTree[i] = new Node(Integer.MAX_VALUE, i - squareNum);
}
for(int i = 2 * squareNum - 1; i > 0; i--){
if(segmentTree[i/2] == null){
segmentTree[i/2] = segmentTree[i];
continue;
}
segmentTree[i/2] = min(segmentTree[i/2], segmentTree[i]);
}
int m = Integer.parseInt(br.readLine());
StringBuilder sb = new StringBuilder();
for(int roop = 0; roop < m; roop++){
st = new StringTokenizer(br.readLine());
int command = Integer.parseInt(st.nextToken());
int i = Integer.parseInt(st.nextToken());
switch(command){
case 1:
int v = Integer.parseInt(st.nextToken());
update(squareNum + i - 1, v);
break;
case 2:
int j = Integer.parseInt(st.nextToken());
int idx = query(squareNum + i - 1, squareNum + j - 1);
sb.append(idx).append("\n");
}
}
System.out.println(sb);
}
/**
* 값을 변경하는 메서드
* @param i 값을 변경하고자 하는 인덱스
* @param v 변경하고자 하는 값
*/
static void update(int i, int v){
segmentTree[i] = new Node(v, segmentTree[i].idx);
for(int roop = i; roop > 1; roop /= 2){ // 2로 나누어가며 반복
if(roop % 2 == 0){
roop++; // 오른쪽으로 맞추게
}
Node origin = segmentTree[roop/2]; // 초기값 기록
segmentTree[roop/2] = min(segmentTree[roop-1], segmentTree[roop]);
if(origin.equals(segmentTree[roop/2])){ // 만약 값이 변경되지 않았다면 더이상 진행할 필요 x
break;
}
}
}
/**
* 질의를 담당하는 메서드
* @param i 구간 시작
* @param j 구간 끝
* @return 최소값으로 탐색된 인덱스
*/
static int query(int i, int j) {
int start = i;
int end = j;
Node min = segmentTree[start]; // 초기값 설정
while(start <= end){
if(start % 2 == 1){ // 오른쪽 자식 노드라면
min = min(min, segmentTree[start]); // min 업데이트
start++; // 부모 노드 변경
}
if(end % 2 == 0){ // 왼쪽 자식 노드라면
min = min(min, segmentTree[end]); // min 업데이트
end--; // 부모 노드 변경
}
start /= 2;
end /= 2;
}
return min.idx;
}
private static Node min(Node leftNode, Node rightNode){
Node minNode = null;
if(leftNode.value != rightNode.value){ // 두 노드의 값이 다르다면
// 값이 작은걸 리턴
minNode = leftNode.value < rightNode.value ? leftNode : rightNode;
return minNode;
}
// 두 노드의 값이 같다면, 인덱스가 작은걸 리턴
minNode = leftNode.idx <= rightNode.idx ? leftNode : rightNode;
return minNode;
}
static class Node{
int value;
int idx;
public Node(int value, int idx) {
this.value = value;
this.idx = idx;
}
}
}
문제 풀이 전략
세그먼트트리의 최소를 만들어가는 트리를 이용하면 단순한 문제였다.
물론, 세그먼트트리를 모른다면 매우 어렵다. 정확히 내용을 모른다면 하단 링크를 참고하자.
[도구정리] 세그먼트 트리(Segment Tree) 알고리즘
진행하는 과정에서 생길 수 있는 부분에 대해서는 Q.라고 적어두고 하단부에 모두 설명할 테니 참고하자. 개념주어진 데이터의 구간 합과 데이터 업데이트를 빠르게 수행하기 위해 고안해낸 자
to-travel-coding.tistory.com
세그먼트트리를 이용하여 최솟값을 업데이트하면 되지만, 실제 정답 출력은 값이 아닌 인덱스를 사용해야 하기에 Node라는 클래스를 별도로 만들어 진행했다.
또한, 업데이트를 하는 경우 값 변경에 따른 최소 노드가 변경될 수 있기 때문에 반드시 자신과 같은 자식 노드를 비교한다.
추가적으로 값이 변경이 없다면 더 이상 진행할 필요가 없으므로 마무리한다.
'문제 풀이 > 백준' 카테고리의 다른 글
| [백준] 1670번. 정상 회담 2(JAVA) (0) | 2025.04.17 |
|---|---|
| [백준] 1132번. 합(JAVA) (1) | 2025.04.16 |
| [백준] 2357번. 최솟값과 최댓값(JAVA) (0) | 2025.04.13 |
| [백준] 11505번. 구간 곱 구하기(JAVA) (0) | 2025.04.13 |
| [백준] 2042번. 구간 합 구하기(JAVA) (0) | 2025.04.13 |