티스토리 뷰

IOI/IOI2018

IOI 2018 Day 2 문제 및 해법

전명우 2018.09.07 17:09

문제: 공식 홈페이지

채점: Yandex

1. doll


0번 정점을 시작점이라고 부르고 0번 정점은 나가는 간선이 정확히 1개다. 1번부터 M번까지 번호가 매겨진 나가는 간선이 정확히 1개인 트리거 정점 M개가 존재한다. 그리고 스위치라고 불리는 정점들은 번호가 음수로, 개수가 S개일 때, -1번부터 -S번까지 번호가 매겨진다. 스위치 정점은 나가는 간선이 정확히 2개이며 스위치 상태가 'X'일 때는 'X'로 표시된 간선으로 나가게 되며, 스위치 상태가 'Y'일 때는 'Y'로 표시된 간선으로 나가게 된다. 정점을 나가면서 동시에 스위치 정점의 상태가 뒤집힌다. 초기 0번 정점에서 이동을 시작하며, 이동을 하다가 다시 처음 0번 정점으로 돌아오는 순간 모든 스위치 정점의 상태는 'X'여야하며 트리거 정점은 정확히 N번 방문해야하며, 방문한 순서가 주어진다. 이렇게 이동할 수 있도록 스위치 정점을 적게 사용하여 회로를 구성하는 문제다.


이 문제에서 제일 핵심이 되는 문장은 "다시 시작점으로 돌아왔을 때 모든 스위치의 상태가 초기 상태와 같아야한다"다. 이 조건을 통해, 스위치들을 정점으로 한 루트가 있는 이진트리를 구성하면 어떨까 라는 생각을 할 수 있다.


문제에서 주어진 예제처럼 트리거 정점의 개수 M=4개, 방문한 트리거 정점의 수 N=4개이며, 방문한 순서는 [1, 2, 1, 3]이라고 하자. 이 때, 다음과 같이 스위치 정점들을 통해 루트가 있는 이진트리를 생각할 수 있다.



위 그림에서 스위치로 구성된 이진트리의 높이 $k=3$이라고 하자 이진트리 높이 $k$는 $2^k \geq N+1$를 만족하는 가장 작은 $k$로 결정한다. 그리고 불필요한 정점들을 최대한 제거한다. 그러면 사용한 스위치의 개수는 $N + \log_{2} N$이하가 되며, -1번 정점에 총 $2^k$번 방문하게 되며 다시 시작점 0번 정점으로 돌아갔을 때 모든 스위치의 상태는 'X'가 되고, 트리거 정점의 방문 순서 또한 주어진대로 만들 수 있다.


이진트리를 그리는 자세한 방법은 코드를 참고하자.


코드 보기


2. highway


N개의 정점과 M개의 간선으로 이루어진 무방향성 그래프가 주어진다. ask 함수를 통해 각 간선의 가중치를 A 혹은 B로 설정했을 때, 정점 S와 정점 T 사이의 최단 거리를 알 수 있다. ask 함수 호출 횟수가 제한적일 때, ask 함수 호출을 통해 적절한 정보를 얻어 정점 S와 T가 어떤 정점인지를 알아내는 문제다.


Subtask 1 (총 5점)

ask 함수는 최대 100번 호출할 수 있고, 주어지는 그래프는 트리이며, N ≤ 100 이고, S = 0이다.


모든 정점에 대해 그 정점이 T인지 확인을 해볼 수 있다. 정점 S와 정점 T 사이의 거리(간선 가중치 거리가 아닌 경로에 있는 간선 개수) $d$는 모든 간선의 가중치를 A로 두고 ask 함수 호출을 하면 알 수 있다. 그리고 정점 i에 대해 정점 T가 되는지 확인을 하는데, 정점 i가 정점 T인지 확인하기 위해서, 정점 S에서 정점 i까지 가는 모든 간선의 가중치를 B로 두고, 나머지 간선의 가중치는 A라고 두었을 때 거리가 $d \times B$가 되면 정점 i는 정점 T가 되는 것이 확실하다. 함수 호출 횟수는 최대 N번이 된다.


Subtask 2 (총 12점)

ask 함수는 최대 60번 호출할 수 있고, 주어지는 그래프는 트리이며, S = 0이다.


정점 S와의 거리를 기준으로 오름차순으로 정점들을 정렬한 다음에, 이분탐색을 통해 정점 T를 찾을 수 있다. 함수 호출 횟수는 최대 $1 + \lceil\log_2 N\rceil = 18$번이 된다.


코드 보기


Subtask 4 (총 51점)

ask 함수는 최대 60번 호출할 수 있고, 주어지는 그래프는 트리이며, S = 0이다.


한 번의 함수 호출을 통해 정점 S와 정점 T 사이의 거리 $d$를 알 수 있다. 그리고 정점 S와 정점 T 사이의 경로에 포함된 한 간선 e를 간선들의 집합에 대한 이분탐색을 통해 찾아낼 수 있다. 그리고 간선 e를 그래프에서 없애주어 그래프를 두 개의 서브트리로 나눈다.


편의상 간선 e가 정점 x와 정점 y를 연결하는 간선이라고 하고, 또한 정점 x가 정점 S와 같은 서브트리 안에, 정점 y가 정점 T와 같은 서브트리 안에 있다고 하자. 정점 x가 있는 서브트리에 대해 Subtask 2와 동일한 상황이 되고, 마찬가지로 정점 y가 있는 서브트리에 대해 Subtask 2의 풀이와 동일하게 정점 S와 정점 T를 각각 독립적이게 찾을 수 있다. 따라서 총 함수 호출 횟수는 최대 $1 + 17 + 16 + 16 = 50$번이 된다.


코드 보기


Subtask 6 (총 100 점)

ask 함수는 최대 50번 호출할 수 있다.


Subtask 4에서와 비슷한 방법으로 정점 S와 정점 T를 연결하는 최단 경로에 속한 임의의 간선 e를 구할 수 있다. 단, 정점 S와 정점 T를 연결하는 최단 경로가 여러 개가 존재할 수 있으므로 Subtask 4에서의 이분탐색을 살짝 보완해야한다.


편의상 간선 e가 정점 x와 정점 y를 연결하는 간선이라고 하자. 그리고 일반성을 잃지 않고 정점 S, x, y, T가 모두 속한 최단 경로에서 각 정점의 방문 순서가 S, x, y, T 순이라고 하자. 정점 S는 정점 y보다 정점 x와 더 가까울 것이며, 정점 T는 정점 x보다 정점 y와 더 가까울 것이다. 정점 y보다 정점 x와 더 가까운 정점들의 집합을 P라고 하고, 그렇지 않은 정점들의 집합을 Q라고 하자. 집합 P와 Q는 서로소 집합이고 집합 P와 집합 Q의 합집합은 전체 정점 집합이 된다. 자명히 정점 S는 집합 P에 있고, 정점 T는 집합 Q에 있다.


집합 P에 있는 정점들을 정점 x와의 최단 거리 기준으로 정렬해서 Subtask 2에서 했던 이분탐색으로 정점 S를 찾을 수 있다. 마찬가지로 집합 Q에 있는 정점들을 정점 y와의 최단 거리 기준으로 정렬해서 이분탐색으로 정점 T를 찾을 수 있다. 정리하면 Subtask 4에서 정점 x의 서브트리를 집합 P로 볼 수 있으며, 정점 y의 서브트리를 집합 Q로 보는 것과 같다. 즉, Subtask 4와 아주 비슷한 상황이 된다. 따라서 Subtask 4에서와 마찬가지로 최대 50번의 호출 안에 문제를 해결할 수 있다.


코드 보기


3. meetings


N개의 산이 있고, 왼쪽부터 오른쪽까지 차례대로 0부터 N-1번까지 번호가 매겨져있다. 그리고 각 산에는 정확히 한 명씩 사람이 살고 있다. Q개의 모임이 있는데, i번째 모임은 L[i]번 산부터 R[i]번 산까지 살고 있는 모든 사람들을 한 곳에 모여야한다. 각 사람이 이동하면서 드는 비용은 이동 경로 위에 놓인 가장 높은 산의 높이만큼이고, 모임의 전체 비용은 모이는 각 사람의 비용 합이다. 문제는 각 모임마다 모임의 최소 비용을 구하는 것이다.


Subtask 2 (총 19점)

N ≤ 5,000, Q ≤ 5,000


각 모임마다 $O(N)$ 시간복잡도로 최소 비용을 구하면 되며, 이는 왼쪽에서 오른쪽으로 가는 방향과 오른쪽에서 왼쪽으로 가는 방향, 두 방향에 대해 스택을 이용하여 비용을 계산할 수 있다. 전체 시간복잡도는 $O(QN)$이다.


코드 보기


Subtask 3 (누적 36점)

N ≤ 100,000, Q ≤ 100,000, H[i] ≤ 2


이 서브태스크에서는 높이가 1 또는 2로 두 종류만 있다. 그리디하게 생각해보면 모임 비용을 최소화하는 모임 장소는 $[L_i, R_i]$ 구간안에 있는 1로만 이루어진 가장 긴 부분구간에 있는 곳이다. Segment tree를 이용해 1로만 이루어진 가장 긴 부분구간의 크기를 구하면 문제를 해결할 수 있으며, 모임 당 시간복잡도는 $O(\lg N)$이고, 전체 시간복잡도는 $O((N+Q) \lg N)$이다.


코드 보기


Subtask 4 (누적 60점)

N ≤ 100,000, Q ≤ 100,000, H[i] ≤ 20


이 서브태스크에서는 높이가 모두 20이하이다. 따라서 다음과 같은 정의를 가지고 있는 배열에 알맞는 값들을 $O(HN)$ 만에 계산할 수 있다.


D[i][j] = i번째 산에서 전체 모임을 가지고 모든 산들의 높이가 j이하라고 할 때, 즉, 높이가 j보다 높은 산들은 높이를 j라고 가정할 때, 모임 비용을 N*j에서 뺀 것


그리고 segment tree를 이용하여 다음 함수의 값을 $O(\lg N)$ 시간에 계산할 수 있다.

get(h, l, r) = l이상 r이하인 i에 대해 D[i][h]의 최대값


구간 $[L_i, R_i]$가 있을 때, 아래 그림처럼 구간에 대해 maximal 부분을 구한다.


각 빨간색 범위에 대해, 그 범위 안에 모임 장소가 있을 경우 최소 모임 비용을 get(h, l, r) 함수를 통해서 $O(\lg N)$ 구할 수 있다. 높이의 범위가 $H$이므로, 생기는 빨간색 범위의 개수는 $O(H)$가 되며, 전체 시간복잡도는 D 배열 값을 구하는 전처리 시간을 포함하여 $O(H(N + Q \lg N))$ 이 된다.


코드 보기


Subtask 5 (총 100점)

N ≤ 750,000, Q ≤ 750,000


풀이 준비 중.


코드 보기


'IOI > IOI2018' 카테고리의 다른 글

IOI 2018 Day 2 문제 및 해법  (0) 2018.09.07
IOI 2018 Day 1 문제 및 해법  (0) 2018.09.05
댓글
댓글쓰기 폼