문제 링크
- https://icpc.me/21825
문제 출처
- 2021 CCO Day1 3번
사용 알고리즘
- DFS
시간복잡도
- $$
풀이
다음과 같은 트리를 생각해 봅시다. 각 정점에서 가장 먼저 사용하는 간선을 화살표로 표현했습니다.
첫 번째 순회에서는 1 2 5 2 6 8 6 2 1 3 1
순서대로 방문합니다. 첫 번째 순회를 한 이후의 트리는 다음과 같습니다. 첫 번째 순회에서 방문한 정점들의 화살표가 모두 첫 번째 자식(없다면 부모) 정점을 가리키고 있다는 점을 관찰할 수 있습니다.
두 번째 순회에서는 1 2 4 2 5 2 6 8 6 2 1 3 7 3 1
순서대로 방문합니다. $t-1$번째 순회에서 방문한 정점은 항상 $t$번째 순회에서 방문한다는 점도 알 수 있습니다. 또한, 각 순회에서 정점을 방문하는 순서가 역전되지 않습니다.
따라서 자식 정점들을 적당한 순서대로 배치한 다음 오일러 투어를 구해 놓으면, 두 정수 $t, k$가 주어졌을 때 $t$번째 순회에서 $k$번째로 방문하는 정점을 세그먼트 트리 등을 이용해 오프라인으로 구할 수 있습니다.
입력으로 트리의 인접 리스트가 주어지면 부모 정점의 위치 $p$를 구합시다. $p$ 이전에 있는 정점은 현재 정점을 처음으로 방문한 순회에서, $p$ 이후에 있는 정점은 그 다음 순회에서 방문하게 됩니다. 따라서 DFS를 이용해 $O(N)$ 시간에 각 정점을 처음으로 방문하는 시점을 구할 수 있습니다. 각 간선을 처음 사용하게 되는 시점도 함께 구할 수 있습니다.
이후 구현의 편의를 위해 부모 정점이 가장 마지막에 오도록 인접 리스트를 cyclic shift 합시다.
이제 $K$번째로 방문하는 정점을 구하는 쿼리를 처리할 차례입니다. 우리의 궁극적인 목표는 $K$를 적당한 정수 $t, k$로 바꿔서, $t$번째 순회에서 $k$번째로 방문하는 정점을 구하는 문제로 변환하는 것입니다.
각 간선이 처음 사용되는 시점을 알고 있다면, 누적 합과 비슷한 방식으로 각 순회의 길이를 상수 시간에 구할 수 있습니다. 따라서 순회의 길이의 누적합 위에서 이분 탐색을 하면 $O(\log N)$ 시간에 $t$를 구할 수 있습니다. $N+1$번째 순회부터는 항상 모든 정점과 간선을 방문한다는 것에 유의하세요.
$t$를 구했다면 $k$를 구하는 것은 간단합니다. 단순히 모듈러 연산을 이용하면 되기 때문입니다.
$t, k$가 주어졌을 때 실제 정답을 찾는 것은 쿼리를 $t$ 오름차순으로 정렬한 다음, 세그먼트 트리에서 $k$번째 원소를 찾는 연산을 사용하면 됩니다. 자세한 구현은 코드의 64번째 줄부터 참고하시면 됩니다.
전체 코드
1 |
|