아쉬움이 남지만
BOJ 18862
https://www.acmicpc.net/problem/18862
정점 의 부모 정점의 번호를 , 정점 의 높이를 라고 하자. 이때 정점 의 조상 정점 에서 정점 까지 점프했을 때의 높이 는 아래와 같이 계산된다.
dfs를 통해 깊이 있는 정점부터 답을 구하면서, 에서 가 유지되도록 하자.
void dfs(int vertex) {
for (int child : adj[vertex]) {
if (child == p(vertex)) continue;
dfs(child, vertex);
}
add(vertex, 2 * h(vertex));
answer[vertex] = count[vertex];
}
이 함수에서 는 의 조상 정점 중에서 점프로 에 도달할 수 있는 정점이 있을 경우 이고, 없을 경우 에서 점프로 도달할 수 있는 정점의 개수가 된다. add 함수는 현재 정점부터 시작하여 서브트리 전체의 배열을 갱신할 것이다. 편의상 모든 정점의 높이가 짝수가 되도록 하기 위해 높이에 2를 곱해주었다.
void add(int vertex, int height) {
if (height == 0) return;
H[vertex] += height;
R[vertex] += height;
for (int child : adj[vertex]) {
if (child == p(vertex)) continue;
if (count[child] == 0) {
add(child, R[vertex] / 2);
count[vertex] += count[child];
count[child] = 0;
}
else if (H[vertex] >= H[child]) {
add(child, (H[vertex] - H[child]) / 2);
count[vertex] += count[child];
count[child] = 0;
}
}
R[vertex] %= 2;
}
은 같은 값이 두 번 더해지는 것을 막기 위한 배열로, 항상 이다. add 함수를 호출하면 가 된다는 것을 계산을 통해 알 수 있으므로, 이 알고리즘은 올바른 답을 구한다.
이 알고리즘의 시간복잡도를 계산해보자. 각 정점 에 대해서, ()가 호출되는 횟수가 임을 보이면 전체 시간복잡도가 임을 보일 수 있다. () 일단 에서 가 호출되려면 가 의 조상이어야 한다. 인 는 개이므로 의 호출도 회이다. 인 경우에는 의 변화량 총 합이 최대 이다. 따라서 의 변화량도 최대 이고, 의 호출도 최대 회이다. 따라서 는 총 회 호출되며, 총 시간복잡도는 가 된다.