문제 : https://www.acmicpc.net/problem/2336
[ 문제 요약 ]
- A가 B보다 3가지 성적이 모두 좋다면 A는 대단한 것이고, 이 A보다 대단한 학생이 없으면 A를 굉장하다고 합니다. 여기서 굉장한 학생의 수를 구하는 것입니다.
[ 테스트 케이스 설명 ]
10 // 학생 수 1<=500,000
2 5 3 8 10 7 1 6 9 4 // 각 랭크별 학생 번호
1 2 3 4 5 6 7 8 9 10 // 각 랭크별 학생 번호
3 8 7 10 5 4 1 2 6 9 // 각 랭크별 학생 번호
[ 문제 해설 ]
문제를 간단하게 말해서 3개 과목 모두 나보다 순위가 작은 사람이 없으면 나는 굉장한 학생이 되는 것입니다.
그래서 입력이 3줄로 들어올 때, 한 학생의 3가지 과목 순위를 담을 객체를 만들어, 각 학생마다 순위들을 몰아서 하나의 객체에 저장해 놓습니다.
그럼 하나의 객체에는 과목(1), 과목(2), 과목(3)의 순위가 각각 들어가 있을 텐데, 이를 과목(1)의 순위 기준으로 오름차순 정렬합니다.
정렬한 후 첫 번째 객체는 무조건 굉장한 학생일 것입니다. 그 이유는 내 첫 번째 과목의 순위가 1등이기 때문에 당연히 나보다 3과목의 순위가 모두 작은 학생이 없을 것이기 때문입니다.
정렬된 배열의 두 번째 객체부터는 확인을 해봐야 합니다.
두 번째 객체는 과목(1)의 순위가 두 번째이기 때문에, 과목(2)와 과목(3)의 순위가 어떤지 파악해야 합니다. 파악을 하는 기준은,
내 왼편에 있는, 즉 이미 탐색한 객체들의 과목(2), 과목(3)의 순위들만 확인하면 됩니다.
이미 과목(1) 기준으로 정렬했기 때문에 내 왼편의 객체들은 과목(1)이 나보다 작다는 것이 확인되었으니,
그들의 과목(2)와 과목(3)의 순위가 내 과목(2)와 내 과목(3)보다 모두 작은 게 있다면 나는 굉장한 학생이 아닐 것입니다.
모두 작은 게 없다면 나는 굉장한 학생이 됩니다.
그럼 내 왼편의 객체들의 과목(2)와 과목(3)의 순위를 어떻게 빠르게 탐색할 수 있을까요?
방법은 세그먼트 트리를 이용하며, 세그먼트 트리의 index는 과목(2)의 값을,
인덱스의 저장하는 value는 과목(3)의 값으로 하고, 최솟값을 구하는 세그먼트 트리를 만들어 해결할 수 있습니다.
[ 정답 코드 ]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
class Main{
static final int MAX = 1<<30;
static int[] tree;
public static void main(String[] args)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine()); // 1 ≤ N ≤ 500,000
Node[] node = new Node[N];
tree = new int[N * 4];
for(int i=0; i<N; i++)
node[i] = new Node(0,0,0);
for(int i=0; i<3; i++)
{
StringTokenizer st = new StringTokenizer(br.readLine());
for(int rnk=1; rnk<=N; rnk++)
{
int index = Integer.parseInt(st.nextToken()) - 1;// 해당 학생의 번호
if(i == 0)
node[index].rank1 = rnk;
else if(i == 1)
node[index].rank2 = rnk;
else
node[index].rank3 = rnk;
}
}
Arrays.fill(tree, MAX);
Arrays.sort(node);
int ans = 0;
for(Node now : node)
{
int min = query( 1, 1, N, 1, now.rank2 );
if(min > now.rank3)
++ans;
update( 1, 1, N, now.rank2, now.rank3 );
}
System.out.print(ans);
}
public static void update(int treeNode, int s, int e, int targetIdx, int value) {
if(e < targetIdx || targetIdx < s)
return;
if(s == e)
{
tree[treeNode] = value;
return;
}
int mid = (s + e) >> 1;
update(treeNode << 1, s, mid, targetIdx, value);
update(treeNode << 1 | 1, mid + 1, e, targetIdx, value);
tree[treeNode] = Math.min(tree[treeNode << 1], tree[treeNode << 1 | 1]);
}
public static int query(int treeNode, int s, int e, int left, int right) {
if(e < left || right < s)
return MAX;
if(left<=s && e<=right)
return tree[treeNode];
int mid = (s + e) >> 1;
int l = query(treeNode << 1, s, mid, left, right);
int r = query(treeNode << 1 | 1, mid + 1, e, left, right);
return Math.min(l, r);
}
}
class Node implements Comparable<Node>{
int rank1, rank2, rank3;
Node(int r1, int r2, int r3){
rank1 = r1;
rank2 = r2;
rank3 = r3;
}
@Override
public int compareTo(Node o) {
return rank1 - o.rank1;
}
}
'알고리즘 > 세그먼트트리' 카테고리의 다른 글
세그먼트 트리의 활용 예시 (0) | 2025.05.24 |
---|---|
BOJ 백준 16993 연속합과 쿼리 (1) | 2025.04.21 |