문제
- 문제 링크
BOJ 28425 - 점수 계산하기
$r$ $v$ $:$ $r$번 노드가 루트일 때 $v$번 직원의 점수$?$
$N$개의 노드로 이루어진 트리와, 위 내용의 $Q$개의 쿼리가 주어진다.
각 쿼리에 맞는 답을 계산하여 출력하자.
TL : $2$ sec, ML : $1024$ MB
$1 ≤ N, Q ≤ 10^5$ $0 ≤ score ≤ 10^4$
알고리즘 분류
- 트리(trees)
- 다이나믹 프로그래밍(dp)
- 트리에서의 다이나믹 프로그래밍(dp_tree)
- 최소 공통 조상(lowest common ancestor)
풀이
쿼리 없이 루트 노드를 $1$로 잡고 정적인 상태에서의 답을 계산해보자.
이는 트리$DP$로 간단히 계산할 수 있다.
그러나 쿼리 내용에 의하면 루트가 계속적으로 바뀔 수 있다.
적당한 사이즈의 트리를 그려놓고 관찰 및 $case$_$work$를 해보면 되는데, 나는 다음의 $3$가지로 결론이 났다.
두번째 경우는 트리의 모양이 뒤집어진다.
바뀐 트리 기준에서 $v$의 부모는, $v→r$의 경로 상에 맨 처음 등장하는 정점$(k)$이 된다.
또한 기존 트리에서 $k$를 루트로 하는 서브 트리 외에 존재하는 모든 정점들이, $v$의 서브 트리에 포함되게 된다.
따라서 답은 $dp[1] - dp[k]$.
$k$는 $r$이 $v$와 깊이 차가 $1$이 될 때까지 끌어올린 정점과 같으므로, $LCA$를 찾을 때와 비슷하게 $Binary$ $Lifting$을 이용해주면 되겠다.
결과적으로 전처리 $O(NlogN)$, 쿼리당 $O(log^2N)$의 복잡도를 가지므로 문제를 $O(Qlog^2N)$에 풀 수 있다.
그 외 $Segment$ $Tree$ $+$ $ETT$를 이용한 풀이나, $Offline$ $Query$로 문제를 해결하는 등 다양한 풀이가 있는 듯 하다.
전체 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #include<bits/stdc++.h> #define N 100001 using namespace std; vector <int> Gr[N]; int depth[N], table[N][18]; int n, q, dp[N]; void Input() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> n >> q; for (int i(1); i <= n; cin >> dp[i++]); for (int i, j, o{}; ++o < n;) { cin >> i >> j; Gr[i].push_back(j); Gr[j].push_back(i); } } void Init(int now, int dep) { depth[now] = dep; for (int& next : Gr[now]) if (!depth[next]) { table[next][0] = now; Init(next, dep + 1); dp[now] += dp[next]; } } void TableDP() { for (int j(1); j < 18; j++) for (int i(1); i <= n; i++) table[i][j] = table[table[i][j - 1]][j - 1]; } int lifting(int x, int diff) { for (int i{}; diff; diff >>= 1, i++) if (diff & 1) x = table[x][i]; return x; } int GetLCA(int x, int y) { if (depth[x] < depth[y]) swap(x, y); x = lifting(x, depth[x] - depth[y]); if (x ^ y) { for (int i(17); !!~i; i--) if (table[x][i] ^ table[y][i]) { x = table[x][i]; y = table[y][i]; } x = table[x][0]; } return x; } void Query() { for (int r, v; q--;) { cin >> r >> v; int lca(GetLCA(r, v)); if (r == v) cout << dp[1] << '\n'; else if (lca == v) { r = lifting(r, depth[r] - depth[lca] - 1); cout << dp[1] - dp[r] << '\n'; } else cout << dp[v] << '\n'; } } int main() { Input(); Init(1, 1); TableDP(); Query(); } | cs |
comment
끝
'Problem Solving > Baekjoon Online Judge (Platinum)' 카테고리의 다른 글
백준 28433 - 게리맨더링 (C++) (0) | 2023.08.09 |
---|---|
백준 13038 - Tree (C++) (0) | 2023.08.07 |
백준 28277 - 뭉쳐야 산다 (C++) (0) | 2023.08.04 |
백준 4787 - Covered Walkway (C++) (0) | 2023.08.04 |
백준 10293 - Ranks in groups (C++) (0) | 2023.07.19 |