문제 링크
- http://icpc.me/13537
사용 알고리즘
- Segment Tree
- Merge Sort Tree (online query)
시간복잡도
- online query
- O(n log n + m log3 n)
- offline query
- O((n+m) log (n+m))
풀이
먼저 online query를 이용한 방식을 설명하겠습니다.
이 문제와 같이 특정 구간에서 k번째 수 ~~~ 혹은 k보다 큰/작은 수 ~~~ 형태의 문제는 Merge Sort Tree라는 것을 이용하여 해결하는 경우가 많습니다.
이 문제를 풀면서 나오는 Segment Tree가 Merge Sort의 진행 과정과 유사하기 때문입니다.
입력된 숫자들을 세그먼트 트리를 이용해 관리를 합니다. 트리의 각 노드는 대응하는 구간의 원소들을 정렬한 배열을 키값으로 갖습니다.
이런 식으로 merge sort tree 형태를 갖게 됩니다.
어떤 구간에서 x 보다 큰 수의 개수는 다음과 같이 재귀적으로 구할 수 있습니다.
- 원하는 구간과 현재 탐색 중인 구간이 전혀 교차하지 않는다면, 0개
- 원하는 구간이 현재 탐색 중인 구간을 완전히 포함한다면, 현재 탐색 중인 구간에서 이진 탐색
- 일부만 교차한다면, 2개의 자식 노드에 대해 재귀적으로 위에 있는 두 연산을 수행
이러한 방식으로 답을 구한다면, Merge Sort Tree를 구축하는데 O(n log n)이 걸리고, O(log3 n)만에 k보다 큰 수를 찾을 수 있습니다.
따라서 전체 시간 복잡도는 O(n log n + m log3 n)입니다.
이번에는 tonyjjw님께서 설명해주신 offline query를 이용한 방식입니다.
먼저, 쿼리를 두 가지 종류로 구분합니다.
- 값을 삽입하는 쿼리
- 해를 구하는 쿼리
쿼리를 모두 입력받은 후, 쿼리를 다음 기준으로 정렬합니다.
- 삽입해야 하는 값 혹은 해를 구해야 하는 값이 큰 순서로
- 값이 같다면 해를 구하는 쿼리가 먼저 오도록
해를 구할 때는 k의 값보다 작거나 같은 값들은 전혀 신경을 쓸 필요가 없습니다.
이 성질을 이용해서 정렬을 해, 아직 필요 없는 값은 전혀 신경쓰지 않게 할 수 있습니다.
시간 복잡도는 쿼리를 정렬하는 데 O((n+m) log (n+m)), 쿼리를 수행하는데 O(n log n + m log n)이 소요되어 총 시간 복잡도는 O((n+m) log (n+m))입니다.
전체 코드
online query
1 |
|
offline query
1 |
|