문제 정보
출처: BOJ 17693 - Port Facility (JOI Spring Training Camp 2017 1-2번)
요약: JOI 항구에 도착하는 $N$개의 컨테이너를 두 개의 보관 구역에 배정합니다. 각 구역은 스택(Stack)처럼 동작하므로, 같은 구역 내 두 컨테이너 $i, j$ ($A_i < A_j$)는 반드시 $B_j < B_i$를 만족해야 합니다(LIFO). 교차하는 구간($(A_i, B_i)$과 $(A_j, B_j)$가 $A_i < A_j < B_i < B_j$ 형태)은 다른 구역에 배정해야 합니다. 가능한 배정 방법의 개수를 $\bmod 10^9+7$로 구하세요.
제한 조건:
- $1 \le N \le 1,000,000$
- $1 \le A_i, B_i \le 2N$, $A_i < B_i$
- 모든 $A_1, \ldots, A_N, B_1, \ldots, B_N$은 서로 다름
- 시간 제한: 4.5초, 메모리 제한: 1024 MB
입출력 형식 및 예제
입력 형식
| |
예제 1
입력:
| |
출력:
| |
설명: 컨테이너 1,3,4를 구역A에, 2를 구역B에 배정하거나 다양한 방식으로 배정 가능 → 4가지
예제 2
입력:
| |
출력:
| |
설명: 모든 구간이 교차하여 홀수 사이클을 형성 → 이분 칠하기 불가 → 0
접근 개요
핵심 관찰 (Circle Graph 이분 칠하기)
문제 재해석: 각 컨테이너를 시간 축 위의 구간 $[A_i, B_i)$로 모델링합니다. 두 구간이 교차(Crossing)하면 같은 구역에 있을 수 없습니다. 즉, 교차 관계를 간선으로 하는 **충돌 그래프(Conflict Graph)**가 이분 그래프인지 판별하고, 이분 칠하기(2-coloring)의 개수를 세는 문제입니다.
이분 칠하기의 개수: 그래프가 이분 그래프라면:
- 각 **연결 요소(Connected Component)**마다 두 가지 칠하기 방식 존재(색 반전)
- 총 개수 = $2^c$ (단, $c$ = 연결 요소 개수)
알고리즘 (Segment Tree + BFS)
$N$이 최대 100만이므로 $O(N^2)$ 간선 구성 불가. 다음 전략 사용:
세그먼트 트리 구축 (2개):
- Tree1: 위치 = $A_i$, 값 = $B_i$, 범위 최댓값 쿼리
- Tree2: 위치 = $B_i$, 값 = $A_i$, 범위 최솟값 쿼리
BFS 색칠:
- 각 미방문 정점에서 BFS 시작, 연결 요소 개수 증가
- 정점 $u$를 방문하면, “교차하는 모든 미방문 이웃"을 쿼리 + 제거
- 이웃: Tree1 쿼리로 $(A_u, B_u)$ 범위에서 $B > B_u$ 찾기, Tree2로 $(A_u, B_u)$ 범위에서 $A < A_u$ 찾기
유효성 검증 (각 색 그룹):
- 각 색 그룹의 구간을 $A$ 정렬 후 스택으로 검증: 구간이 교차하지 않아야 함
알고리즘 설계
자료구조
세그먼트 트리 2개 (총 2배 복잡도):
tree1[node]: 인덱스 = 시작 시간 $A_i$, 값 = 종료 시간 $B_i$, 범위 최댓값 유지tree2[node]: 인덱스 = 종료 시간 $B_i$, 값 = 시작 시간 $A_i$, 범위 최솟값 유지
색 배열:
color[i]= 0 또는 1 (미방문 = -1)매핑 배열: 시간 좌표 ↔ 컨테이너 ID 변환
구현 포인트
| 항목 | 설명 |
|---|---|
| 구간 교차 판정 | $A_i < A_j < B_i < B_j$ 형태 감지 |
| Tree 쿼리 + 제거 | 범위 쿼리 후 해당 잎 노드 값을 “무효값” 설정 + 상위 노드 업데이트 |
| BFS 종료 조건 | 모든 정점 방문, 이분 칠하기 검증(홀수 사이클 없음) |
| 답 계산 | 이분 그래프 ⇒ $2^{\text{components}} \bmod 10^9+7$, 아니면 0 |
정당성
Claim: 그래프가 이분 그래프 ⟺ 각 색 그룹이 교차 없음(스택 정렬 가능)
근거:
- 교차 없는 구간들은 완전히 중첩되거나 분리되므로 스택으로 관리 가능
- 역으로, 교차가 생기면 그래프에 홀수 사이클 발생 ⇒ 이분이 아님
시간 복잡도 분석:
- 각 정점은 최대 1회 큐에 삽입 (방문 시 세그먼트 트리에서 제거)
- Tree 노드 방문: 각 정점당 $O(\log N)$
- 총: $O(N \log N)$
복잡도 분석
| 항목 | 복잡도 | 근거 |
|---|---|---|
| 시간 | $O(N \log N)$ | 세그먼트 트리 구축 $O(N \log N)$ + BFS 방문 + Tree 쿼리/제거 |
| 공간 | $O(N)$ | 세그먼트 트리 (크기 $4N$) + 색/매핑 배열 |
실제 정답 코드
| |
코너 케이스 체크리스트
| 케이스 | 처리 방법 | 예상 결과 |
|---|---|---|
| N=1 | 단일 구간, 이분 칠하기 가능 | $2^1 = 2$ |
| 완전 중첩 (e.g., $[1,8], [2,7], [3,6]$) | 모두 교차 → 홀수 사이클 → 이분 불가 | 0 |
| 분리된 구간들 (e.g., $[1,2], [3,4]$) | 교차 없음 → 독립 컴포넌트 | $2^{\text{# components}}$ |
| 경계값 (e.g., $A_i = 1, B_i = 2N$) | 전체 범위 포함 | 교차 판정 정상 작동 |
| 두 구간만 (교차) | 간선 하나 → 이분 그래프 | $2^1 = 2$ |
| 모두 교차 (홀수 사이클) | 완전 그래프 $K_3$ 이상 → 홀수 사이클 | 0 |
제출 전 점검
입출력 형식
- 입력 첫 줄:
N(정수) - 다음 N줄: 각 컨테이너
A B(공백 구분) - 출력: 한 줄에 정수 (modulo $10^9+7$)
- 개행 문자 확인 (
"\n"사용)
오버플로우 체크
- 답 계산: $2^N$ 최대 $2^{1000000}$ → 반드시 modulo 연산
-
ans = (ans * 2) % MOD형태로 매 단계 적용
초기화 및 범위
-
posA, posB인덱스: 1-indexed (컨테이너 ID) - Tree 인덱스: 1-indexed, 좌표 범위 $[1, 2N]$
-
color[]초기화: -1 (미방문) -
tree1초기값: -INF,tree2초기값: INF
알고리즘 검증
- BFS 종료: 모든 정점 방문 또는 이분 칠하기 실패
- 색 충돌 감지: 같은 색 이웃 발견 시 → 홀수 사이클 → 0 반환
- 스택 정렬 검증: 각 색 그룹이 교차 없는지 최종 확인
세그먼트 트리 정합성
-
findRemove함수: 제거 후 상위 노드 업데이트 필수 - 범위 경계:
(uA + 1, uB - 1)(strict 부등호) - 제한 값: Tree1은
> limitB, Tree2는< limitA확인
유사 문제 및 참고 자료
- BOJ 2610 - Hierarchy (컴포넌트 개수 세기)
- BOJ 3197 - Lake (Two Pointer + DSU)
- 개념 참고:
- Circle Graph Coloring, Interval Scheduling
- Segment Tree Range Query + Lazy Deletion
- BFS-based Graph Coloring
![Featured image of post [Algorithm] C++ 백준 17693번: Port Facility](/post/algorithm/2025-12-04-boj-17693-port-facility-cpp-solution/wordcloud_hu_898dca7a384c403f.png)
![[Algorithm] C++ 백준 16983번: Coin Collecting](/post/algorithm/2025-12-04-boj-16983-coin-collecting-cpp-solution/wordcloud_hu_59f275e060977e1b.png)
![[Algorithm] C++ 백준 17682번 Tents](/post/algorithm/2025-12-04-boj-17682-tents-combinatorics-cpp-solution/wordcloud_hu_2726d57ba0310c29.png)
![[Algorithm] C++ 백준 17693번: Port Facility](/post/algorithm/2025-12-04-boj-17693-port-facility-cpp-solution/wordcloud_hu_1d04909fb0d36e18.png)
![[Algorithm] C++ 백준 18526번: Bomas](/post/algorithm/2025-12-04-boj-18526-bomas-cpp-solution/wordcloud_hu_e648c602797aff58.png)
![[Algorithm] C++ 백준 23575번: Squid Game](/post/algorithm/2025-12-04-boj-23575-squid-game-cpp-solution/wordcloud_hu_3623649df8b22837.png)
![[Algorithm] C++/Python 백준 12735번: Boat](/post/algorithm/2025-08-14-boj-12735-boat-cpp-python-solution/wordcloud_hu_dfad17dd2b1a530d.png)
![[Algorithm] C++ 백준 16746번: Four-Coloring](/post/algorithm/2025-12-04-boj-16746-four-coloring-cpp-solution/wordcloud_hu_4d8a2c47b9c8078f.png)
![[Algorithm] C++ 백준 15782번: Calculate! 2](/post/algorithm/2025-12-02-boj-15782-calculate-2-tree-segtree-lazy-cpp-solution/wordcloud_hu_c7b151aaf16cbe10.png)
![[Algorithm] C++/Python 백준 16993번: 연속합과 쿼리 (세그먼트 트리)](/post/algorithm/2025-09-16-boj-16993-maximum-subarray-queries-segment-tree-cpp-python-solution/wordcloud_hu_789f7bcec4a582b7.png)
![[Algorithm] C++ 백준 14897번: 서로 다른 수와 쿼리 1](/post/algorithm/2025-08-14-boj-14897-distinct-in-range-queries-1-cpp-solution/wordcloud_hu_fcf9b2f0117a9599.png)