본문 바로가기

알고리즘/Baekjoon

구간 트리. [12899]

[1. 문제 설명]

아래 쿼리를 수행하는 프로그램을 작성한다.

 

A. 수열 S 에 자연수 X 를 추가한다. (append)

B. 수열 S 에 포함된 숫자 중 X 번째로 작은 수를 출력하고, 수열에서 제거한다.


[2. 풀이 접근]

간단한 풀이를 생각해보면, 트립(treap) 의 적용을 생각해 볼 수 있다.

 

트립을 통해 원소의 삽입과 삭제를 logN 에 할 수 있다.

또한 K번째 원소도 쉽게 찾을 수 있다.

그러나 구현이 살짝 까다로울 수 있으니, 나중에 다시 정리해보도록 하고,


구간 트리를 사용하여 풀이를 해볼 수 있다.

자연수 X 의 범위는 1 이상 2,000,000 이하이므로,

구간을 [1, 2,000,000] 을 하는 구간 트리를 생각 해 볼 수 있다.

 

각 노드가 저장하는 값은 구간이 포함하는 원소의 개수 이다.

 

예를 들면 아래와 같다.

구간 [1, 5] 가 3개의 원소를 포함하는데,

[1, 3] 구간은 2개의 원소 [4, 5] 구간은 1개의 원소를 포함한다.

 

root 노드를 통해 전체 몇개의 원소가 수열에 있는지 알 수 있으며,

leaf 노드에 저장 된 값이 하나 이상인 경우 leaf 가 가리키는 구간이 수열에 저장된 원소라 볼 수 있다.

(하나 이상이라는 것은 중복 된 원소가 수열에 저장되어 있음을 의미한다.)

 

즉, 쿼리 A 는 leaf 노드에 저장된 값에 +1 하고,

이 후, 구간 합을 갱신하도록 하면 된다.

 

쿼리 B 에 대한 처리는 아래와 같이 생각 해 볼 수 있다.

자연수 범위가 5 까지로 제한 된다고 하고,

수열에 [1, 2, 3, 4, 5] 가 저장되어 있다고 해보자

 

여기서 3번째 원소를 구하는 과정은 아래와 같다.

(노드에 적힌 숫자는 구간에 속한 원소의 개수)

루트를 기준으로 3번째 숫자는 왼쪽 트리에 존재한다.

=> 왼쪽으로 이동.

 

루트의 왼쪽 자식노드를 기준으로 3번째 숫자는 오른쪽 트리에 존재한다.

=> 오른쪽으로 이동

=> 단말 노드 => 3을 반환.

 

이번에는 4번째 숫자를 찾는 과정이다.

루트를 기준으로 4번째 숫자는 오른쪽 트리에 존재한다.

=> 오른쪽으로 이동

=> 루트를 기준으로 4번째 숫자는 오른쪽 자식 노드를 기준으로 첫번째 숫자이다.

 

오른쪽 자식 노드를 기준으로 1번째 숫자는 왼쪽 트리에 존재한다.

=> 왼쪽으로 이동

=> 단말 노드 => 4를 반환. 

 

즉, 현재 노드를 기준으로 X 번째 노드를 찾는 것 인데,

왼쪽, 오른쪽에 저장 된 구간 합을 기준으로

어느쪽 으로 갈지 선택하는 것이며,

오른쪽으로 갈 때는 X 를 오른쪽을 기준으로 보정해야 한다.

 

 

마지막으로 생각할 부분은 수열에 중복된 값이 푸쉬 될 때이다.

이 경우는 간단하게 생각 할 수 있는데,

leaf 노드에 저장 된 값은 leaf 가 가리키는 구간(구간의 길이는 1 이므로 수열 내 원소를 의미함) 의 개수를 의미함으로,

이 값을 단순히 1만큼 증가시켜서 전체 트리를 업데이트 하면 된다.

(반대로 쿼리 B 를 실행하면 1 감소 시켜야 한다.)


[3. 코드]

 

'알고리즘 > Baekjoon' 카테고리의 다른 글

스위핑. [2170]  (0) 2022.11.04
구간 트리. [1168]  (0) 2022.11.03
구간 트리. [16975]  (0) 2022.11.01
구간 트리. [9345]  (0) 2022.10.31
구간 트리. [1517]  (0) 2022.10.30