티스토리 뷰
문제에 쓰여있는 그대로 R를 루트노드라고 하고 LCA(U, V)를 구해주면 끝인 문제이다. 이 문제를 처음 봤을 때 LCA의 sparse table를 입력마다 갱신하는 방법이 바로 떠오르겠지만 그럴 경우 코드를 작성하지 않아도 안된다는 것을 알 수 있다. 그럼 어떻게 해야 할까
이 그림은 위 문제의 테스트케이스를 그래프로 나타낸 것이다.
이때 (U, V)에 대해 R의 위치는
1. R 이 LCA(U, V) 보다 위에 있을 때,
2. R 이 V -> LCA(U, V) 사이에 있는 노드와 연결되어 있을 때,
3. R 이 U -> LCA(U,V) 사이에 있는 노드와 연결되어있을 때,
4. R이 루트 노드를 거쳐 U, V를 가야 할 때
이렇게 총 4개가 나오게 된다.
그럼 처음부터 따져보자.
위 그림은 1번 경우에 대한 그림인데, 이런 상태에선 당연히 R을 루트로 한 LCA(U, V)는 LCA(U,V) 그대로 일것이다. R 이 LCA(U,V) 부터 그 위로 있으면 루트를 변경해도 차이가 없다.
위 그림은 2번 경우에 대한 그림인데, V -> LCA(U,V) 사이에 R이 있으므로 R이 루트 일 때는 LCA(U, V)가 R 되거나 R 이 다른 간선을 통해 연결되어 있을 때 LCA(V, R) 이 된다.
3번 같은 경우도 위와 같은 방식으로 생각하면 바로 알 수 있다.
마지막 4번 같은 경우도 1번 경우처럼 LCA를 벗어났기 때문에 어떤 노드가 R이 된다 하더라도 LCA 에는 변함이 없다.
그러므로 1번과 4번을 합친 다음 2,3번을 제외한 나머지는 모두 LCA 그대로 출력하게 해 줘도 무방하다.
이 경우들을 LCA를 3번 돌린 다음 앞에서 설명했던 방법으로 비교하면서 알맞은 값을 출력해 주면 된다.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=18; // 2의 18승이 100,000 을 넘음
ll N,M;
ll parent[100000][MAX]; // i 의 2^k 번째 부모
ll depth[100000]; // 깊이
ll cost[100000];
vector<ll>adj[100000]; // 그래프 vector
// dfs로 트리만들고 parent[i][0], depth 채움
void dfs(ll curr){
for(int i=0;i<adj[curr].size();i++){
ll next=adj[curr][i];
if(depth[next]==-1){
parent[next][0]=curr;// 2^0 = 1 이므로 자신의 부모를 나타냄
depth[next]=depth[curr]+1; // 부모의 깊이에서 +1
cost[next]=cost[curr]+adj[curr][i];
dfs(next);
}
}
}
ll lca(ll u,ll v){
// depth[u] >= depth[v] 가 되도록 u,v 를 변경
if(depth[u]<depth[v])swap(u,v);
int diff = depth[u]-depth[v]; // 깊이를 같게 하기 위해서 차이를 구함
// 11을 이진수로 나타냈을 때 1011 이 나오는데, 이진수를 구하는 과정을 역으로 해보면
// 2^3 * 1 + 2^2 * 0 + 2^1 * 1 + 2^0 * 1 로 나타나기 때문에 각 자리의 숫자와 2^j 를 곱하면 11을 얻을 수 있게 됨
int j=0;
while(diff!=0){
if(diff%2==1)u=parent[u][j];
diff/=2;
j++;
}
// u,v 가 일정 높이로 같아짐
// u,v의 2^j 번째 부모 노드가 다를 때 일정 높이 만큼 이동
if(u!=v){
// 2^j 에서 j를 감소시키면서 찾아감 , 2^a개를 뛰어넘는다면 다음에는 2^a이상으로 넘어갈 수 없기 때문에 이를 이용하여 a를 줄여가며 최종적으로 같아짐
for(int j=MAX-1;j>=0;j--){
if(parent[u][j]!=-1 && parent[u][j]!=parent[v][j]){
u=parent[u][j];
v=parent[v][j];
}
}
// 위 과정을 다 거치면 lca 의 자식에 위치하게 되므로 한번 올려줌
u=parent[u][0];
}
return u;
}
// 0-base
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> N;
for(int i=0;i<N-1;i++){
ll u,v,w;
cin >> u >> v;
u--;
v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
//reset
for(int i=0;i<100000;i++){
fill(parent[i],parent[i]+MAX,-1);
depth[i]=-1;
cost[i]=-1;
}
depth[0]=0;
cost[0]=0;
dfs(0);
for(int j=0;j<MAX-1;j++){
for(int i=1;i<N;i++){ // i=0 는 루트 노드라 안함
if(parent[i][j]!=-1){
parent[i][j+1]=parent[parent[i][j]][j];
}
}
}
//parent 채우기
cin >> M;
for(int i=0;i<M;i++){
ll u,v,q;
cin >> q >> u >> v;
u--;v--;q--;
ll UR=lca(u,q), VR=lca(v,M),UV=lca(u,v);
//cout << UR << ' ' << VR << ' ' << UV << "\n";
if(UR==UV&&VR==UV){
cout << VR+1 << "\n";
}else if(VR==UV&&UR!=UV){
cout << UR+1 << "\n";
}else{
cout << UV << "\n";
}
}
}
'ps' 카테고리의 다른 글
BOJ 2493(탑) 풀이 (0) | 2023.07.31 |
---|---|
2023 COI 고등부 예선 풀이 (0) | 2023.05.26 |
BOJ 25402(트리와 쿼리)풀이 (0) | 2022.09.09 |
BOJ 12722(Mousetrap (Large))풀이 (0) | 2022.08.04 |
BOJ 22878(간단한 문제)풀이 (0) | 2022.02.11 |
- Total
- Today
- Yesterday
- 이분 탐색
- 완전 탐색
- 느리게 갱신되는 세그먼트 트리
- 세그먼트 트리
- 수학
- Python
- 그래프 탐색
- 자료구조
- 그리디 알고리즘
- KOI
- 트리에서의 다이나믹 프로그래밍
- A Dance of Fire and Ice
- 최소 스패닝 트리
- 누적 합
- 선분 교차 판정
- 그래프 이론
- 이분매칭
- 개발
- 트리
- 알고리즘
- C++
- 정렬
- BOJ
- discord bot
- 구현
- 깊이 우선 탐색
- 자료 구조
- 잡봇
- 다이나믹 프로그래밍
- codeforces
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |