서론
2026년 7월 18일에 개최된 2026 한국정보올림피아드 2차대회 문제 풀이입니다.
| 1번 | 2번 | 3번 | 4번 | |
|---|---|---|---|---|
| 초등부 | 거리두기 | 주사위 탑 쌓기 | 간식 분배 | 게임 |
| 중등부 | 주사위 탑 쌓기 | 간식 분배 | 게임 | 수열 연산 |
| 고등부 | 간식 분배 | 극솟값 제거 | 곡예 | 공장 |
네트워크 플로우는 KOI 역사상 처음, 이분 매칭을 포함하더라도 2007년 이후로 두 번째로 등장했습니다. IOI Syllabus에는 $O(E\cdot \vert F\vert)$ Ford-Fulkerson을 이용한 최대 유량의 계산과 max-flow min-cut theorem이 2025년(link)부터 추가되었습니다. 2021년 중등부에 접미사 배열이 나온 것을 보면 KOI가 꼭 IOI Syllabus 안에 있는 것만 출제하는 것 같진 않지만…
고등부 2번과 3번은 만점을 받는 방법이 여러 개 있습니다. 모두 재미있고 교육적으로도 유익한 내용이므로 공부하는 것을 추천합니다.
고등부 4번을 제외한 다른 문제는 만점 풀이와 관련 있는 서브태스크만, 고등부 4번은 모든 서브태스크를 설명합니다. PC로 보는 경우 오른쪽 사이드바에서 원하는 문제로 이동할 수 있습니다.
초등부 1번. 거리두기
모든 $1 \le i < N$에 대해 $B_{i+1} - B_i = K$인 경우만 고려해도 충분합니다. $B_{i+1} - B_i > K$인 $i$가 존재한다면, $B_{i+1}, B_{i+2}, \cdots, B_N$을 각각 1씩 감소해도 $B_1$을 유지하면서 올바른 해를 만들 수 있기 때문입니다. 따라서 $B_1$의 값을 $x$로 만들 수 있는지 확인하는 건, 모든 $1 \le i \le N$에 대해 $B_i = x + (i-1)K \le A_i$인지 확인하면 됩니다.
어떤 정수 $x$가 주어졌을 때 $O(N)$ 시간에 $x$가 답이 될 수 있는지 확인할 수 있으므로, 전체 프로그램의 시간 복잡도는 $x$의 탐색 범위에 따라 결정됩니다. 항상 $B_1 \le A_1 \le \max A_i$가 성립하므로 $x$는 최대 $\max A_i \le 100$까지만 확인하면 됩니다. 반대로, $A_N = 1$이면 $B_N = x + (N-1)K = 1$이 되어야 하고, 따라서 $1 - (N-1)K \le x$ 범위는 탐색해야 한다는 것을 알 수 있습니다. 넉넉하게 $-NK \le x \le \max A_i$ 범위를 탐색한다고 하더라도 시간 안에 문제를 해결할 수 있습니다.
$O(N)$ 시간에도 문제를 해결할 수 있습니다. 모든 $1 \le i \le N$에 대해 $B_i = x+(i-1)K \le A_i$가 성립하는 $x$의 최댓값을 찾으면 되는데, 식을 정리하면 $x \le A_i - (i-1)K$ 가 되고, 이는 $\min_{1 \le i \le N} \lbrace A_i - (i-1)K \rbrace$를 구하는 것과 같습니다. 따라서 $O(N)$ 시간에 $x$로 가능한 최댓값을 구한 뒤, 다시 $O(N)$ 시간에 수열 $B$를 복원하여 문제를 해결할 수도 있습니다.
1 | |
초등부 2번/중등부 1번. 주사위 탑 쌓기
1과 6으로 탑을 만드는 문제, 2와 5로 탑을 만드는 문제, 3과 4로 탑을 만드는 문제, 이렇게 3개의 독립된 문제로 나눠서 생각해도 됩니다. $i$가 나온 주사위가 $x$개, $7-i$가 나온 주사위가 $y$개 있을 때 탑의 개수를 최소화하는 상황을 생각해 봅시다. 일반성을 잃지 않고 $x \ge y$ 인 상황만 생각할 것입니다.
$x = y$ 이면 x - y - x - y - ... - x - y 형태의 탑 1개로 주사위를 모두 쌓을 수 있습니다. 마찬가지로 $x = y + 1$ 일 때도 x - y - x - y - ... - x 형태의 탑 1개로 주사위를 모두 쌓을 수 있습니다. 여기에서 관찰할 수 있는 것은, 한 개의 탑으로는 $i$와 $7-i$가 나온 주사위를 같은 개수로 없애거나, 한쪽 주사위를 하나 더 가져갈 수 있다는 점입니다.
따라서 $x - y = 0$이면 1개의 탑으로, $x - y = d > 0$ 이면 $d$개의 탑으로 모든 주사위를 쌓을 수 있습니다. $i$와 $7-i$가 나온 주사위가 모두 0개일 때를 조심해서 구현해야 합니다.
1 | |
초등부 3번/중등부 2번/고등부 1번. 간식 분배
$N$명의 학생과 $N$개의 간식이 있으므로, 모든 학생이 간식을 가져가기 위해서는 각 학생이 한 개의 간식만 가져가야 합니다. 따라서 현재 남은 간식 중 $i$번 학생이 좋아하는 간식의 수를 $Deg(i)$라고 정의했을 때, $Deg(i) = 1$인 학생을 방에 들여보내는 그리디 전략을 생각해 볼 수 있습니다. 그리고 해가 존재하는 경우, 이 그리디 전략이 항상 해를 찾음을 증명할 수 있습니다.
위 관찰을 했다면 만점 풀이는 위상 정렬과 비슷한 방식으로 어렵지 않게 작성할 수 있습니다. 각 학생이 좋아하는 간식 목록(A[i])과, 각 간식을 좋아하는 학생 목록(B[i])를 저장한 뒤, $Deg(i) = 1$인 학생 $i$가 나타날 때마다 큐에 넣는 방식으로 구현하면 $O(N+\sum C_i)$ 시간에 문제를 해결할 수 있습니다.
1 | |
초등부 4번/중등부 3번. 게임
Subtask 4. $k_1 = k_2 = \cdots = k_Q$ (21점)
고정된 $k$에 대해 문제를 해결하는 방법을 먼저 생각해 봅시다.
현재 정점 $v$에 연결되어 있는 간선 중, Alice가 승리할 수 있다고 알려진 정점으로 가는 간선이 $k$개 이상이라면 $v$에서도 Alice가 승리할 수 있습니다. 따라서 다음과 같은 방식을 생각해 볼 수 있습니다.
- 출구가 있는 정점을 win position으로 표시
- 어떤 정점 $v$에서 win position인 정점으로 가는 간선이 $k$개 이상이면 $v$도 win position으로 표시
- 더 이상 win position에 추가되는 정점이 없을 때까지 반복
위상 정렬과 비슷하게, win position 정점이 추가될 때마다 큐에 넣는 식으로 구현하면 $O(N+M)$ 시간에 각 정점이 win position인지 판별할 수 있습니다. 따라서 $k$가 고정되어 있는 경우 $O(N+M+Q)$ 시간에 답을 구할 수 있습니다.
1 | |
Subtask 7. 추가 제약 조건 없음 (100점)
$(s, k)$에서 Alice가 승리한다면 $(s, k-1)$에서도 Alice가 승리합니다. 따라서 각 정점 $s$에 대해, $s$에서 Alice가 승리하는 최대 $k$를 찾으면 쿼리를 상수 시간에 처리할 수 있습니다. 그러한 $k$를 $D(s)$라고 합시다. $A_i = 1$이면 $D(i) = \infty$입니다. 각 쿼리 $(s, k)$의 답은 단순히 $D(s) \ge k$인지 확인하는 것으로 구할 수 있습니다.
$D(\ast)$의 값이 큰 정점부터 차례대로 처리할 것입니다.
$D(v) = c$로 확정되었다고 합시다. 이는 모든 $k \le c$에 대해 $v$에서 Alice가 승리할 수 있음을 의미합니다. $v$와 인접하면서 아직 $D(i)$의 값이 확정되지 않은 $i$가 있고, $v$와 $i$를 잇는 간선이 $w$개 있다고 합시다.
기존에 $i$가 확보한 win position으로 가는 간선을 이용해 $k \le D(i)$ 범위에서 승리를 보장할 수 있었다면, $w$개의 간선이 추가되면서 그 값은 최대 $D(i) + w$까지 증가할 수 있어 보입니다. 하지만 새로 추가된 간선은 정점 $v$로 가는 간선이기 때문에, $v$에서 Alice가 이길 수 있는 $k \le c$ 에서만 사용할 수 있습니다. 따라서 $v$에서 $i$로 가는 간선이 $w$개 있다면, $D(i) \leftarrow \max(D(i), \min(c, D(i)+w))$로 갱신할 수 있습니다.
위 과정은 다익스트라 알고리즘과 유사한 방식으로 계산할 수 있습니다. max heap을 이용해 $D(\ast)$의 값이 가장 큰 정점 $v$를 뽑은 뒤 $D(v)$를 확정시키고, $v$에서 확정되지 않은 정점 $i$로 가는 간선을 이용해 정보를 갱신하면 $O((N+M) \log M)$ 시간에 $D(\ast)$를 모두 계산할 수 있습니다.
1 | |
중등부 4번. 수열 연산
Subtask 3. $M = N$ (12점)
1번 연산(교환 연산)만 사용해서 $A$와 $B$를 같게 만들어야 합니다.
어떤 수 $x$가 $B$에서 등장하는 위치를 $P(x)$라고 정의합시다. 만약 $i < j$ 이면서 $A_i > A_j$라면, $A_i$와 $A_j$의 상대적인 순서는 바뀔 수 없습니다. 따라서 $i < j, A_i > A_j$ 이면 $P(A_i) < P(A_j)$가 성립해야 하고, 이 조건이 필요충분조건입니다.
위 조건이 성립할 때 실제로 $A$를 $B$로 변환하는 것은, $A$의 왼쪽에 있는 원소부터 차례대로 보면서 삽입 정렬을 수행하면 됩니다. 조건을 확인하는 것, 그리고 삽입 정렬을 수행하는 것 모두 $O(N^2)$ 시간에 가능합니다.
1 | |
Subtask 5. $M = N-1$ (25점)
$B$에 포함되지 않은 원소가 하나 존재합니다. 2번 연산(합치기 연산)을 이용해 그 원소를 제거한 뒤 Subtask 3의 문제를 해결하면 됩니다.
$B$에 포함되지 않은 원소를 $A_p = x$라고 합시다. $x$ 를 없애기 위해서는 $x$와 $x$보다 작은 원소가 인접하도록 만든 뒤 2번 연산을 적용해야 합니다. $x$보다 작은 원소가 왼쪽에 있는 경우와 오른쪽에 있는 경우를 나눠서 생각해 봅시다.
$A_p$보다 오른쪽에 있는 $A_p$보다 작은 원소 중 가장 왼쪽에 있는 원소를 $A_q = y < x$ 라고 합시다. 이러한 $q$가 존재한다면, 정의에 의해 $A_{p+1}, A_{p+2}, \cdots, A_{q-1}$은 모두 $x, y$보다 큽니다. 따라서 1번 연산을 $q-p-1$번 이용해 $x$를 $A_{q-1}$로 옮긴 뒤, 2번 연산을 적용하면 $x$를 없앨 수 있습니다.
$A_p$보다 오른쪽에 $A_p$보다 작은 원소가 없는 경우, 즉 $A_p$가 suffix min인 경우만 남았습니다. 이 경우에는 $A_p$보다 작은 원소를 왼쪽에서 찾아야 합니다. $A_p$보다 왼쪽에 있으면서 $P(\ast)$가 가장 큰 수를 $A_r = z$라고 합시다. 만약 $z < x$ 라면 $A_1, A_2, \cdots, A_{p-1}$을 $P(\ast)$ 오름차순으로 정렬한 뒤, 2번 연산을 적용하면 $x$를 없앨 수 있습니다.
$z > x$ 지만 $A_p$ 왼쪽에 $A_p$보다 작은 수 $A_s = w$가 있을 수 있습니다. 하지만 $A_s$와 $A_p$ 사이에 $A_p$보다 큰 수가 있으므로 $x$를 왼쪽으로 옮길 수 없고, $w$를 $A_p$ 바로 왼쪽으로 옮기면 나중에 $P(\ast)$ 오름차순이 되도록 남은 수를 정렬할 수 없습니다. 따라서 $z > x$ 면 $A = B$가 되도록 만들 수 없습니다.
따라서 $A = B$로 만들기 위해 필요한 조건은 다음과 같습니다.
- $i < j$, $A_i > A_j$이고 $A_i,A_j \in B$ 이면 $P(A_i) < P(A_j)$
- $A_i = x \not \in B$ 이면서 $A_i$가 suffix min이면, $A_i$보다 왼쪽에 있는 $P(\ast)$가 가장 큰 원소 $A_r = z$에 대해 $z < x$.
Subtask 8. 추가 제약 조건 없음 (100점)
Subtask 5와 같은 방법으로 해결할 수 있습니다. 여기에서는 구현 방법에 대해 조금 더 자세하게 설명합니다.
먼저 $B$에 없는 원소 중 $A$에서 suffix min이 아닌 원소를 모두 없앱니다. $A$를 뒤에서부터 차례대로 보면서 suffix min이 아닌 원소 $A_i = x$를 만날 때마다, $x$보다 더 작은 원소를 만날 때까지 1번 연산을 이용해 뒤로 보낸 뒤, 2번 연산으로 $x$를 없앨 수 있습니다. 각 원소가 최대 $O(N)$번 뒤로 이동할 수 있으므로, 이 단계의 시간 복잡도는 $O(N^2)$입니다.
이제, $A$에 남은 원소를 앞에서부터 차례대로 보면서, 현재 원소가 $B$에 포함된 원소라면 삽입 정렬을 이용해 $P(\ast)$ 오름차순이 되도록 위치를 옮깁니다. 만약 현재 원소가 $B$에 포함되지 않았다면, 이미 바로 왼쪽에 $P(\ast)$가 가장 큰 원소가 있는 상태이므로 바로 2번 연산을 적용하면 됩니다. 이 단계 또한 $O(N^2)$ 시간에 모두 끝낼 수 있습니다.
시간 복잡도가 $O(N^2)$라는 것은 쉽게 알 수 있지만, 1번 연산과 2번 연산의 시행 횟수가 $N^2$번 이하인지는 다시 확인하는 게 좋습니다.
- 교환 연산은 항상 수열의 inversion을 늘리는 방향으로만 수행할 수 있습니다. 길이가 $N$인 수열에서 가능한 inversion의 최댓값은 ${N\choose 2} = N(N-1)/2$이므로, 교환 연산은 최대 $N(N-1)/2$번 수행할 수 있습니다.
- 합치기 연산을 수행하면 수열의 길이가 1 감소합니다. 길이가 $N$인 수열에서 원소를 $N-M$개 삭제해야 하므로, 합치기 연산은 정확히 $N-M$번 수행해야 합니다.
$N \ge 1$이면 항상 $N(N-1)/2 + N-M \le N^2$이 성립합니다.
1 | |
고등부 2번. 극솟값 제거
Subtask 2. $l = 1, r = N$ (17점)
$l = 1, r = N$인 문제의 풀이를 먼저 생각해 봅시다. $A_{i-1} > A_i < A_{i+1}$인 $A_i$를 모두 한 번에 삭제하는 연산은, 1번 정점과 $N$번 정점이 아닌 카르테시안 트리의 리프 정점을 모두 삭제하는 것이라고 생각할 수 있습니다. 카르테시안 트리에서 $i$번 정점의 높이를 $H_i$라고 정의합시다(리프 정점이면 $H_i = 1$). 연산을 $t$번 연달아 수행하면, 1번 정점과 $N$번 정점의 조상을 제외하고 $H_i \le t$인 정점들만 모두 사라집니다.
$A_i$보다 오른쪽에 있으면서 $A_i$보다 큰 원소 중 가장 왼쪽에 있는 원소의 위치를 $R(i)$라고 합시다. 다시 말해, $R(i) = \min_{i < j, A_i < A_j} j$입니다. 마찬가지로 $L(i) = \max_{i>j, A_i<A_j} j$도 정의합시다. 카르테시안 트리에서 1번 정점의 조상은 $1, R(1), R^2(1), \cdots$이고, $N$번 정점의 조상은 $N, L(N), L^2(N), \cdots$입니다. 따라서 $l = 1, r = N$ 일 때 제거되지 않는 정점은 다음과 같습니다. (단, $m$은 최댓값의 위치)
- $H_i > t$인 정점
- $1, R(1), R^2(1), \cdots, m$ 중 $H_i \le t$인 정점 (prefix max)
- $N, L(N), L^2(N), \cdots, m$ 중 $H_i \le t$인 정점 (suffix max)
- $H_m \le t$이면 (2)와 (3)에서 두 번 세었으므로 답을 1 줄여야 함
$H$, $L$, $R$ 모두 monotone stack을 이용하면 $O(N)$ 시간에 구할 수 있습니다. 이후 (1), (2), (3) 모두 누적 합 배열을 이용하면 $O(N)$ 시간 전처리 후 매 쿼리마다 $O(1)$ 시간에 계산할 수 있습니다. 따라서 $O(N+Q)$ 시간에 $l = 1, r = N$인 문제를 해결할 수 있습니다.
(2)와 (3) 대신 카르테시안 트리에서 1번 정점과 $N$번 정점을 잇는 경로 위에 있는 정점을 본다고 생각해도 $l = 1, r = N$인 문제를 해결할 수 있지만, 이후 단계로 발전시키기 어렵습니다.
1 | |
만점 풀이 - Persistent segment tree
구간 쿼리가 주어지더라도 똑같습니다. 쿼리 $(l, r, t)$가 주어졌을 때, 연산으로 인해 없어지지 않는 정점은 다음과 같습니다. (단, $m$은 $[l, r]$ 구간에서 최댓값의 위치)
- $l \le i \le r$ 이면서 $H_i > t$인 정점
- $l, R(l), R^2(l), \cdots, m$ 중 $H_i \le t$인 정점 (구간의 prefix max)
- $r, L(r), L^2(r), \cdots, m$ 중 $H_i \le t$인 정점 (구간의 suffix max)
- $H_m \le t$이면 (2)와 (3)에서 두 번 세었으므로 답을 1 줄여야 함
$l$과 $r$이 고정되지 않아서 Subtask 2와 같이 누적 합 배열을 이용할 수 없습니다.
(1)은 Persistent segment tree로 해결할 수 전형적인 형태이므로, $O(N \log N)$ 전처리 후에 매 쿼리마다 $O(\log N)$ 시간에 계산할 수 있습니다.
(2)에서 $i \leftarrow R(i)$ 간선을 만들면 포레스트가 나오므로, $l, R(l), R^2(l), \cdots, m$에서 $H_i \le t$인 정점의 수를 구하는 것은 트리의 경로에서 $H_i \le t$인 정점의 수를 구하는 것이라고 생각할 수 있습니다. 따라서 (2) 또한 PST를 이용하면 $O(N \log N)$ 전처리 후에 매 쿼리마다 $O(\log N)$ 시간에 계산할 수 있습니다. (3)도 같은 방법으로 계산할 수 있습니다.
따라서 전체 문제를 $O((N+Q) \log N)$ 시간에 해결할 수 있습니다. (2)에서 $LCA(m, l) = m$이라는 것을 이용하면 구현을 조금 더 편하게 할 수 있습니다.
만약 Subtask 2에서 prefix max와 suffix max 대신 $l$과 $r$을 잇는 경로로 접근했다면, Subtask 7에서는 경로 위의 정점이 $[l, r]$ 구간을 벗어나게 되어서 2D PST가 필요해 집니다.
1 | |
만점 풀이 - Sparse table
- $l \le i \le r$ 이면서 $H_i > t$인 정점
- $l, R(l), R^2(l), \cdots, m$ 중 $H_i \le t$인 정점 (구간의 prefix max)
- $r, L(r), L^2(r), \cdots, m$ 중 $H_i \le t$인 정점 (구간의 suffix max)
- $H_m \le t$이면 (2)와 (3)에서 두 번 세었으므로 답을 1 줄여야 함
쿼리를 오프라인으로 처리할 수 있다면, 모든 쿼리를 $t$ 내림차순으로 정렬해서 (1)을 단순히 Segment tree나 Fenwick tree만 이용해 처리할 수 있습니다. 또한, (2)에서 $H_l, H_{R(l)}, H_{R^2(l)}, \cdots, H_m$가 증가하는 수열이라는 점을 관찰하면, PST 대신 Sparse table을 이용한 이분 탐색(binary lifting)으로 각 쿼리를 $O(\log N)$에 처리할 수 있습니다. 따라서 PST 를 구현하지 않고도 $O((N+Q) \log N)$ 시간에 문제를 해결할 수 있습니다.
1 | |
만점 풀이 - 2D Segment tree
$l = 1, r = N$일 때 $A_i$가 없어지는 시점을 $T(i)$라고 정의합시다. 카르테시안 트리에서 $1$ 또는 $N$의 조상이면 $T(i) = \infty$, 그렇지 않으면 $T(i) = H_i$입니다.
쿼리 $(l, r, t)$에서 $A_i$가 제거될 조건은 $L(i) \ge l, R(i) \le r, T(i) \le t$입니다. 따라서 각 원소 $A_i$를 3차원 좌표 공간 상의 점 $(N-L(i)+1, R(i), T(i))$라고 정의하면, 쿼리 $(l, r, t)$는 $(N-l+1, r, t)$에 dominate 되는 점의 수를 세는 것으로 생각할 수 있습니다.
점과 쿼리를 x좌표 순으로 정렬하면 x좌표를 무시할 수 있게 되므로, 쿼리로 $(y, z)$가 주어지면 $py \le y, pz \le z$인 점 $(py, pz)$의 수를 세는 문제가 됩니다. 2D Segment tree나 2D Fenwick tree를 이용하면 $O((N+Q) \log^2 N)$ 시간에 문제를 해결할 수 있습니다.
사용하는 좌표를 미리 모두 구할 수 있다는 점을 이용하면, 2D Fenwick tree의 공간 복잡도를 $O(N^2)$이 아닌 $O(N \log N)$으로 만들 수 있습니다. 자세한 방법은 아래 코드에서 fenwick_tree와 fenwick_tree_2d 함수의 coord 관련 로직을 확인하시길 바랍니다.
1 | |
만점 풀이 - CDQ Divide and Conquer
위에 있는 2D Segment tree 풀이에서 이어집니다. 똑같이 점과 쿼리를 3차원 점으로 만든 뒤, x좌표를 기준으로 정렬해 부등호를 3개에서 2개로 줄입니다.
$\text{DnC}(l,r)$은 $l, l+1, \cdots, r$번째 점/쿼리만 고려했을 때 없어지는 점의 개수를 구하는 함수입니다. 이는 아래와 같이 세 단계로 나눠서 계산할 수 있습니다. (단, $m = \lfloor\frac{l+r}{2}\rfloor$)
- $\text{DnC}(l,m)$
- $\text{DnC}(m+1,r)$
- $[l, m]$ 구간에 속한 점과 $[m+1, r]$ 구간에 속한 쿼리 간의 결과
(1)과 (2)는 단순히 DnC 함수를 재귀 호출하는 것으로 처리할 수 있으므로, (3)만 해결하면 됩니다.
$[l, m]$ 구간에 속한 점을 y좌표 오름차순으로, $[m+1, r]$ 구간에 속한 쿼리를 y좌표 오름차순으로 정렬한 뒤, 투 포인터를 이용하면 부등호를 하나 더 줄여서 1차원 구간 쿼리로 바꿀 수 있습니다. 따라서 Fenwick tree 하나만 이용해 답을 구할 수 있습니다. \text{DnC}(l, r)의 끝부분에서 merge sort와 비슷한 방식으로 구간에 속한 점/쿼리를 y좌표 오름차순으로 정렬하면, std::sort와 같은 정렬 함수를 호출하지 않고 빠르게 x좌표 오름차순으로 정렬된 배열을 y좌표 오름차순으로 정렬되도록 수정할 수 있습니다.
시간 복잡도는 $T(N) = 2T(N/2) + O(N \log N) \in O(N \log^2 N)$입니다.
이런 식으로 분할 정복할 때 왼쪽에 있는 원소들이 오른쪽에 있는 원소에 주는 영향을 계산하는 테크닉을 CDQ Divide and Conquer 라고 부릅니다. CDQ의 어원은 중국의 2008년 IOI 금메달리스트이자 현재 프린스턴대학교 컴퓨터학과 교수인 Chen Danqi 입니다.
1 | |
고등부 3번. 곡예
본격적으로 문제 풀이에 들어가기 전에, 몇 가지 관찰을 먼저 해야 합니다.
Observation 1. 구간을 확장하는 점프만 고려해도 된다.
Alice는 오른쪽으로, Bob은 왼쪽으로 이동할 수 있습니다. 구간을 좁히는 것은 자유롭게 할 수 있으므로, 점프대를 통해 구간을 얼마나 많이 넓힐 수 있는지 구하는 것에 집중해야 합니다. 즉, Alice와 Bob이 각각 $a, b$에 있을 때, 아래 두 가지 형태의 점프만 봐도 충분합니다.
- $y < a \le x < b$ 인 점프대 $(x, y)$: 구간의 왼쪽 끝점을 확장
- $a < x \le b < y$ 인 점프대 $(x, y)$: 구간의 오른쪽 끝점을 확장
Observation 2. $[a, b]$에서 최대로 확장할 수 있는 구간 $[L, R]$을 계산해 두면, 쿼리는 $L \le c < d \le R$을 판별하기만 하면 된다.
이 명제가 참이라는 것을 보이기 위해서는 $[a, b]$에서 시작해 도달 가능한 maximal한 구간이 모두 같음을 보여야 하지만, 이 글에서는 증명을 생략합니다.
Observation 3. $[i, i+1]$ 형태의 구간만 고려해도 된다.
$[i, i+1]$에서 시작해 도달 가능한 최대 구간을 $[L_i, R_i+1]$라고 합시다. $[a, b]$에서 시작해 도달 가능한 최대 구간은 $\bigcup_{i=a}^{b-1} [L_i, R_i+1]$과 같고, 이는 $[\min_{i=a}^{b-1} L_i, 1 + \max_{i=1}^{b-1} R_i]$입니다.
Subtask 3. $N \le 3\,000$ (21점)
Alice와 Bob이 각각 $i, i+1$에 있는 상태를 $i$번 상태라고 합시다. 총 $N-1$개의 상태가 있으며, 각 상태에서 한 번의 점프로 갈 수 있는 상태로 간선을 연결하면 정점이 $N-1$개인 방향 그래프를 얻을 수 있습니다.
$i$번 상태에서 Alice가 한 번의 점프로 갈 수 있는 가장 왼쪽 상태를 $L_i$, Bob이 한 번에 점프로 갈 수 있는 가장 오른쪽 상태를 $R_i$(위치는 $R_i+1$)라고 하면, $i$번 정점에서 $L_i, L_i+1, \cdots, R_i$번 정점으로 가는 간선을 만들어야 합니다. 따라서 그래프의 간선은 최대 $O(N^2)$개입니다.
이후 각 정점에서 도달 가능한 정점 번호의 최솟값($S_i$)과 최댓값($E_i$)을 구해야 하는데, 이는 SCC를 하나의 정점으로 압축해서 DAG를 만든 다음, DP를 이용해서 $O(V+E)$ 시간에 계산할 수 있습니다. $V \in O(N), E \in O(N^2)$ 이므로 시간 복잡도는 $O(N^2)$이 됩니다.
이후 쿼리를 처리하는 것은, $L = \min_{i=a}^{b-1} S_i$, $R = 1 + \max_{i=a}^{b-1} E_i$를 계산한 뒤 $L \le c < d \le R$인지 판별하면 됩니다. Sparse table을 이용하면 전처리 $O(N \log N)$ 이후 RMQ를 $O(1)$ 시간에 할 수 있으므로, 전체 시간 복잡도는 $O(N^2 + Q)$입니다.
1 | |
만점 풀이 - SCC, $O(N \log N + Q)$
위 풀이에서 문제가 되는 부분은 그래프의 간선이 $O(N^2)$개라는 것입니다. 간선 개수를 줄이면서 그래프를 온전히 표현할 수 있을까요?
$i$에서 어떤 구간 $[L_i, R_i]$로 가는 간선을 만드는 것이므로, 세그먼트 트리를 생각해 볼 수 있습니다. 리프 정점이 $N-1$개인 세그먼트 트리를 만든 뒤, 부모 정점에서 자식 정점으로 가는 간선을 만듭시다.
이후 $i$에서 구간 $[L_i, R_i]$로 이동할 수 있음을 표현할 때 간선을 $R_i-L_i+1$개 만드는 것 대신, 세그먼트 트리에서 구간 $[L_i, R_i]$를 나타내는 $O(\log N)$개의 정점으로 가는 간선을 만듭니다. 리프 정점 간의 도달 가능성을 그대로 보존하면서, 동시에 매번 만들어지는 간선의 개수를 $O(N)$에서 $O(\log N)$으로 줄일 수 있습니다. 따라서 정점이 $O(N)$개, 간선이 $O(N \log N)$개인 그래프를 만들 수 있습니다. 조금 더 구체적으로는 정점이 최대 $4N$개, 간선이 최대 $2N\lceil \log N \rceil$개인 그래프를 만들 수 있습니다.
$O(N \log N)$ 시간에 SCC를 구하고 $S_i$와 $E_i$를 계산한 뒤 Sparse table을 $O(N \log N)$ 시간에 만들면, 각 쿼리의 답을 상수 시간에 구할 수 있습니다. 전체 시간 복잡도는 $O(N \log N + Q)$입니다.
1 | |
만점 풀이 - SCC, $O(N + Q)$
Subtask 3의 풀이에서 문제가 되는 부분은 그래프의 간선이 $O(N^2)$개라는 것입니다. 위에서 본 $O(N \log N)$ 풀이는 그래프의 도달 가능성을 유지하면서 간선의 개수를 줄이는 방법이었습니다. 도달 가능성을 유지하는 것을 포기하면 어떤 일이 생길까요?
우리가 실제로 필요한 정보는 각 정점에서 갈 수 있는 정점 번호의 최솟값과 최댓값이지, 도달 가능한 정점들의 전체 리스트가 필요하지는 않습니다. 정점 $v$에서 아래 두 정점으로 가는 간선만 만든, $N-1$개의 정점과 $2N-2$개의 간선으로 구성된 그래프를 생각해 봅시다.
- $p_v = \text{argmin}_{L_v \le k \le R_v} L_k$
- $q_v = \text{argmax}_{L_v \le k \le R_v} R_k$
즉, $v$에서 한 번의 점프로 갈 수 있는 $[L_v, R_v]$ 구간의 정점 중, $L_k$가 최소인 정점과 $R_k$가 최대인 정점으로 가는 간선, 이렇게 2개만 만드는 것입니다.
Subtask 3에서 만든 그래프를 $G$, 이번에 만든 그래프를 $H$, 그래프 $H$에서 $v$에서 도달 가능한 정점의 집합을 $Reach_H(v)$라고 정의합시다. 이렇게 두 개의 간선만 만들어도 도달 가능한 최소/최대 정점을 계산할 수 있음을 보여야 합니다. 다시 말해, $U = \bigcup_{x \in Reach_H(v)} [L_x, R_x]$가 $Reach_G(v)$와 같음을 보여야 합니다.
먼저 $Reach_G(v) \subseteq U$임을 보이겠습니다.
$v$에서 한 번의 점프로 갈 수 있는 정점 $x$ ($L_v \le x \le R_v$) 로 가면, $[L_x, R_x]$ 구간에 있는 정점으로도 갈 수 있어집니다. 정의에 의해 $L_{p_v} \le L_x \le R_x \le R_{q_v}$가 성립합니다. 또한, $p_v \in [L_v, R_v] \cap [L_{p_v}, R_{p_v}]$, $q_v \in [L_v,R_v] \cap [L_{q_v}, R_{q_v}]$가 성립하므로, 세 구간 $[L_{p_v}, R_{p_v}]$, $[L_v, R_v]$, $[L_{q_v}, R_{q_v}]$의 합집합은 끊어지지 않은 하나의 구간 $[L_{p_v},R_{q_v}]$를 이루며, 이 구간은 $U$에 포함됩니다. 그러므로 $v$에서 두 번 점프해서 도달 가능한 정점은 모두 $U$에 포함됩니다.
그래프 $H$에서 새롭게 도달한 정점마다 같은 과정을 반복합니다. 어떤 정점 $y$에 도달했다면, $G$에서는 $[L_y, R_y]$ 구간의 모든 정점으로 이동할 수 있고, 이 구간의 정점에서 한 번의 점프로 갈 수 있는 정점은 모두 $[L_{p_y}, R_{q_y}]$ 안에 포함됩니다. 그래프 $H$에는 $y$에서 이러한 두 정점 $p_y, q_y$으로 가는 간선이 있으므로, $p_y, q_y \in Reach_H(v)$입니다. 앞에서 본 것과 마찬가지로 $[L_{p_y}, R_{q_y}] \subseteq U$ 라는 것을 알 수 있습니다. 따라서 $U$ 안의 임의의 정점 $z \in [L_y, R_y]$에서 한 번 더 이동해도 $[L_{p_y}, R_{q_y}]$ 안에만 머무르게 되므로 여전히 $U$ 안에 포함됨을 알 수 있습니다.
이 과정을 반복하면 $v$에서 3번, 4번, 또는 그 이상 점프해서 도달할 수 있는 정점도 모두 $U$에 포함됨을 알 수 있습니다. 즉, $Reach_G(v) \subseteq U$입니다.
이제 반대 방향, 즉 $U \subseteq Reach_G(v)$ 임을 보입니다.
$E(H) \subseteq E(G)$ 라는 것을 생각하면 $Reach_H(v) \subseteq Reach_G(v)$임은 쉽게 알 수 있습니다. 모든 정점 $x \in Reach_H(v)$에 대해 $x$에서 $[L_x, R_x]$의 모든 정점으로 가는 간선이 있으므로, $U \subseteq Reach_G(v)$ 또한 성립합니다.
$O(N)$ 시간 전처리 후에 RMQ를 $O(1)$ 시간에 구할 수 있는 자료구조를 사용하면 그래프를 만드는 데 $O(N)$ 시간이 걸리고, SCC와 DP에도 $O(N)$, 그리고 쿼리를 위한 RMQ를 준비하는 데에도 $O(N)$ 시간이 걸립니다. 이후 쿼리는 매번 상수 시간에 처리할 수 있으므로, 전체 시간 복잡도는 $O(N+Q)$입니다.
1 | |
만점 풀이 - RMQ, $O(N \log^2 N + Q)$
Observation 3에서 이어집니다. $L_i$와 $R_i$를 구해야 합니다.
상태 $i$에서 $k$번 점프해서 갈 수 있는 상태의 범위를 $[f^k(i), g^k(i)]$라고 합시다. $f^k(\ast)$와 $g^k(\ast)$를 모두 알고 있을 때, $f^{2k}(\ast)$와 $g^{2k}(\ast)$를 구할 수 있다면 문제를 빠르게 해결할 수 있어 보입니다.
$i$에서 $2k$번 점프해서 갈 수 있는 상태의 범위는, $[f^k(i), g^k(i)]$ 범위 안에 있는 임의의 상태에서 $k$번 점프해서 갈 수 있는 상태들의 합집합과 같습니다. 따라서 RMQ를 이용해 다음과 같이 계산할 수 있습니다.
- $f^{2k}(i) = \min_{x=f^k(i)}^{g^k(i)} f^k(x)$
- $g^{2k}(i) = \min_{x=f^k(i)}^{g^k(i)} f^k(x)$
Sparse table이나 Segment tree를 이용하면 $k \rightarrow 2k$ 전이를 $O(N \log N)$ 시간에 할 수 있습니다. $k = 1$에서 시작해 $O(\log N)$ 단계를 거치면 $O(N \log^2 N)$ 시간에 $L_i$와 $R_i$를 구할 수 있습니다. 쿼리는 SCC 풀이와 마찬가지로 Sparse table을 이용하면 $O(N \log N)$ 전처리 후에 매번 $O(1)$ 시간에 답을 구할 수 있습니다.
전체 시간 복잡도는 $O(N \log^2 N + Q)$입니다.
1 | |
다른 풀이
이밖에도 적당한 전처리를 한 뒤 쿼리의 결과를 캐싱하는 식으로 경로 압축과 비슷하게 구현하면 실제로 $O(Q + N\sqrt Q)$개의 $(a, b)$ 쌍만 캐싱하게 되어 $O((N+M+Q+N\sqrt Q) \log N)$ 정도 시간에 문제를 푸는 방법이 있고, 스택을 이용해 정점과 간선이 $O(N)$개인 그래프를 만드는 $O(N+Q)$ 풀이도 존재합니다.
고등부 4번. 공장
Subtask 1. $N \le 20$ (13점)
직원을 선택하는 $O(2^N)$가지 경우를 모두 확인하면서, 각 케이스마다 $O(N \log N)$ 정도에 점수를 계산하면 됩니다. 이 서브태스크를 해결하면서 $D_i$가 같은 직원은 항상 짝수 명씩 고용해야 한다는 점을 알아가면 좋습니다.
이렇게 지문이 복잡하고 등장하는 변수가 많은 경우, 완전 탐색을 구현해 보면서 문제를 명확하게 이해하는 것도 좋은 전략입니다.
1 | |
Subtask 2. $T = 1$ (27점)
0일차 밤, 1일차 낮, 1일차 밤만 고려하면 됩니다. 모든 직원을 $A$ 내림차순으로 정렬합시다.
직원을 앞에서부터 보면서, 각 직원을 선택할지 말지 결정하는 DP를 생각합시다. 구체적으로, 점화식을 아래와 같이 정의할 수 있습니다.
- $D(i, 0) = $ $1, \cdots, i$번 직원 중 짝수 명 선택했을 때 가능한 최대 점수
- $D(i, 1) = $ $1, \cdots, i$번 직원 중 홀수 명 선택했을 때 가능한 최대 점수
$i$번째 직원이 홀수 번째로 선택된 직원이라면, 한 번의 야간 근무에서 $A_i$ 만큼의 야간 수당, 주간 생산에서 $B_i$ 만큼의 생산 이익, 그리고 기본 임금이 $C_i$ 만큼 발생합니다. 따라서 점수에 $-2A_i + B_i - C_i$ 만큼 기여합니다.
$i$번째 직원이 짝수 번째로 선택된 직원이라면, 한 번의 야간 근무에서 $-A_i$ 만큼의 야간 수당, 주간 생산에서 $-B_i$ 만큼의 생산 이익, 그리고 기본 임금이 $C_i$ 만큼 발생합니다. 따라서 점수에 $2A_i - B_i - C_i$ 만큼 기여합니다.
따라서 점화식은 아래와 같이 계산할 수 있습니다.
- $D(i, 1) = \max \lbrace D(i-1, 1), D(i-1, 0) - 2A_i + B_i - C_i \rbrace$
- $D(i, 0) = \max \lbrace D(i-1, 0), D(i-1, 1) + 2A_i - B_i - C_i \rbrace$
동적 계획법은 $O(N)$ 이지만, 직원을 정렬해야 하므로 전체 시간 복잡도는 $O(N \log N)$입니다.
1 | |
Subtask 3. $T \le 10$ (47점)
Subtask 2의 풀이와 비슷합니다. $T \le 10$으로 매우 작으므로, 각 날짜마다 고용된 직원 수의 홀짝을 비트로 관리하는 비트 DP를 구현하면 $O(N2^T)$ 시간에 문제를 해결할 수 있습니다.
1 | |
Subtask 4. 각 날짜마다 지원자 8명 이하 (69점)
각 날짜마다 지원자가 8명 이하라는 점을 이용해 비트 DP를 설계해 봅시다. 편의상 $M = 8$이라고 합시다.
$i$일차 지원자 선발 여부가 $bit$일 때, ($i$일차 낮에 얻는 생산 이익) - ($i$일차 지원자의 기본 임금)의 값을 $Day(i, bit)$라고 정의합시다. 각 날짜별로 지원자를 $A$ 내림차순으로 미리 정렬해 두면 $O(M2^MT)$ 시간에 $Day(i, bit)$를 모두 계산할 수 있습니다. 사실 크기가 짝수인 집합만 봐도 되므로 연산량은 절반이 됩니다.
$i$일차 지원자 선발 여부가 $x$, $i-1$일차 지원자 선발 여부가 $y$일 때, $i$일차 밤에 지급해야 하는 야간 수당을 $Nighg(i, x, y)$라고 정의합시다. $O(M^24^MT)$ 시간에 계산할 수 있고, 마찬가지로 크기가 짝수인 집합만 보면 연산량은 1/4가 됩니다.
$D(i, bit) := $ $i$일차의 지원자 선발 여부가 $bit$일 때, $0$일차 밤부터 $i$일차 낮까지 얻을 수 있는 최대 점수라고 정의합시다. $i$일차 밤에 지급되는 야간 수당은 $i+1$일차 지원자가 결정되어야 알 수 있으므로 $D(i, bit)$에서는 $i$일차 낮까지만 고려합니다.
상태 전이는 $D(i, x) \leftarrow D(i-1, y) + Day(i, x) - Night(i, x, y)$와 같이 표현할 수 있습니다. 점화식은 $O(4^MT)$ 시간에 계산할 수 있습니다.
따라서 $O(N \log N + M^2 4^M T)$ 정도 시간에 전체 문제를 해결할 수 있습니다.
1 | |
Subtask 5. 추가 제약 조건 없음 (100점)
TODO: 풀이 작성 예정
아래는 $O(V^2E)$ Dinic algorithm을 사용한 코드지만, DFS를 이용한 $O((V+E)F)$ 또는 $O(V^2F)$ Ford-Fulkerson method의 구현이나 $O(V^2E)$ Edmonds-Karp algorithm을 사용하더라도 시간 안에 여유롭게 문제를 해결할 수 있습니다.
1 | |