인접한 원소끼리만 교환해서 정렬하는 데 필요한 최소 교환 횟수는 수열의 역전 쌍(Inversion) 수와 정확히 일치한다.
값이 최대 999,999,999에 달하므로 좌표 압축 후 **BIT(Binary Indexed Tree)**로 O(N log N)에 역전 쌍을 셀 수 있다.
문제 정보
문제 링크: https://www.acmicpc.net/problem/4297
문제 요약:
- n개의 서로 다른 정수로 이루어진 수열이 주어진다.
- 인접한 두 원소를 교환하는 연산만으로 오름차순 정렬하려 할 때, 최소 교환 횟수를 구한다.
- 여러 테스트 케이스가 주어지며 n = 0이 입력되면 종료한다.
제한 조건:
- 시간 제한: 1초
- 메모리 제한: 256MB
- \(1 \le n < 500{,}000\)
- \(0 \le a[i] \le 999{,}999{,}999\) (모든 값이 서로 다름)
입출력 예제
입력 1:
| |
출력 1:
| |
설명: 첫 번째 수열 [9, 1, 0, 5, 4]에서 역전 쌍은 (9,1), (9,0), (9,5), (9,4), (1,0), (5,4) 총 6쌍이다. 두 번째 수열 [1, 2, 3]은 이미 정렬되어 있으므로 0이다.
접근 방식
핵심 관찰: 인접 교환 최소 횟수 = 역전 쌍 수
인접한 두 원소를 교환하는 연산은 정확히 역전 쌍 하나를 제거한다. 따라서 수열을 정렬하는 데 필요한 최소 인접 교환 횟수는 수열 내 역전 쌍의 총 개수와 동치이다.
역전 쌍 \((i, j)\): \(i < j\)이면서 \(a[i] > a[j]\)인 쌍
값의 범위가 \(10^9\)이므로 BIT에 직접 넣을 수 없다. 따라서 좌표 압축으로 원래 값을 1~n 범위로 변환한 뒤, BIT를 활용한다.
BIT를 이용한 역전 쌍 카운팅 원리
왼쪽에서 오른쪽으로 원소를 차례로 처리할 때, i번째 원소 a[i]를 BIT에 삽입하기 전에 a[i]보다 큰 값이 앞에 몇 개 삽입되어 있는지를 계산하면 된다.
query(a[i]): 1~a[i] 범위에 삽입된 원소 수- i번째 원소를 처리하는 시점에 앞서 삽입된 총 원소 수: i (0-indexed에서 i)
- 역전 기여 =
i - query(a[i])
알고리즘 설계 (Mermaid Flowchart)
flowchart TD
A["입력 n (n=0이면 종료)"] --> B["n개 원소 입력"]
B --> C["좌표 압축정렬 후 unique으로 중복 제거lower_bound로 rank 부여"]
C --> D["BIT 초기화"]
D --> E["i = 0부터 n-1까지 반복"]
E --> F["역전 기여 += i - query(a[i])"]
F --> G["update(a[i])"]
G --> H{"i == n-1?"}
H -- "No" --> E
H -- "Yes" --> I["역전 쌍 수 출력"]
I --> A
단계별 로직
- 좌표 압축: 입력 배열을 복사해 정렬 후 중복 제거.
lower_bound로 각 원소의 rank(1~n)를 구한다. - BIT 초기화: 크기 n+1 배열을 0으로 초기화한다.
- 역전 쌍 카운팅: 원소를 순서대로 처리하면서, 현재까지 삽입된 수 중 현재 값보다 큰 수의 개수(
i - query(a[i]))를 누적한다. - 업데이트: 처리한 원소를 BIT에 삽입한다.
- 출력: 누적된 역전 쌍 수를 출력한다.
복잡도 분석
| 항목 | 복잡도 | 비고 |
|---|---|---|
| 시간 복잡도 | \(O(N \log N)\) | 좌표 압축 정렬 \(O(N \log N)\) + BIT 처리 \(O(N \log N)\) |
| 공간 복잡도 | \(O(N)\) | 입력 배열, 압축 배열, BIT 배열 |
코너 케이스 및 실수 포인트
| 케이스 | 설명 | 처리 방법 |
|---|---|---|
| n=1 | 원소가 하나면 역전 쌍 없음 | 루프가 자연스럽게 0을 반환 |
| 이미 정렬된 수열 | 역전 쌍 0 | i - query(a[i])가 항상 0이 되어 자동 처리 |
| 역정렬된 수열 | 최대 \(N(N-1)/2\)개의 역전 쌍 | long long 사용 필수 (\(500000 \times 499999 / 2 \approx 1.25 \times 10^{11}\)) |
| 값 범위 초과 | 최대 999,999,999 | 좌표 압축 후 BIT 사용 |
| 다중 테스트 케이스 | BIT를 매번 초기화해야 함 | fill(bit, bit + N + 2, 0) 사용 |
구현 코드 (C++)
| |
![Featured image of post [Algorithm] C++ 백준 4297번: Ultra-QuickSort](/post/algorithm/2026-02-23-boj-4297-ultra-quicksort-inversion-count-bit-cpp-solution/wordcloud_hu_a803784e8a771f86.png)
![[Algorithm] C++ 백준 16879번: 궁전 게임](/post/algorithm/2026-02-23-boj-16879-palace-game-grundy-cpp-solution/wordcloud_hu_55fc22f48e24eba1.png)
![[Algorithm] C++ 백준 17408번: 수열과 쿼리 24](/post/algorithm/2026-02-23-boj-17408-sequence-and-query-24-segment-tree-cpp-solution/wordcloud_hu_92a1c52739d8fca5.png)
![[Algorithm] C++ 백준 4297번: Ultra-QuickSort](/post/algorithm/2026-02-23-boj-4297-ultra-quicksort-inversion-count-bit-cpp-solution/wordcloud_hu_c3866fddd244cf7c.png)
![[Algorithm] C++ 백준 17481번: 최애 정하기](/post/algorithm/2026-02-06-boj-17481-favorite-member-bipartite-matching-hopcroft-karp-cpp-solution/wordcloud_hu_9802b5ca1a7b476c.png)
![[Algorithm] C++ 백준 21814번: United Cows of Farmer John](/post/algorithm/2026-02-06-boj-21814-united-cows-of-farmer-john-fenwick-cpp-solution/wordcloud_hu_4a91d245f991ea37.png)
![[Algorithm] C++ 백준 15517번: Array Manipulation at Moloco (Hard)](/post/algorithm/2026-01-30-boj-15517-array-manipulation-at-moloco-hard-fenwick-cpp-solution/wordcloud_hu_521deba1ba8bdbdd.png)
![[Algorithm] C++ 백준 3006번: 터보소트](/post/algorithm/2025-12-19-boj-3006-turbosort-cpp-solution/wordcloud_hu_178815acefb3fc18.png)
![[Algorithm] C++ 백준 19646번: Random Generator](/post/algorithm/2026-02-05-boj-19646-random-generator-fenwick-tree-order-statistics-cpp-solution/wordcloud_hu_8da99d30b7997346.png)
![[Algorithm] C++ 백준 16745번: What Goes Up Must Come Down](/post/algorithm/2025-12-19-boj-16745-what-goes-up-must-come-down-cpp-solution/wordcloud_hu_b15c634ebf63e5a5.png)