반응형
여기보면 개념자체는 간단한다.
$O(N)$ 나이브 구현
더깊은 애부터 하나씩 올려서 만나는데 까지 해보는 방식으로 해보면 된다.
구현도 트리로 인한 복잡성은 약간 있지만, 나이브한 편이고 내가 구현해본 건 다음과 같다. 이 문제의 답안이기도 하다.
|
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
|
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
using vvi = vector<vi>;
#define REP(i,n) for(int i=0;i<(int)(n);i++)
const int INF = (int)1e9;
//무방향그래프를 인풋으로 받아, 특정 정점을 루트를 가지는 방향그래프(트리)로 만들어준다.
struct result { vector<vi> tree; int root; vi parent, level; };
result treefy(vector<vi>& adj_list, int root = -1, bool base1 = false) {
int max_v = (int)adj_list.size();
vector<vi> tree(max_v);
if (root == -1)
{
//find root(왼쪽이 parent인경우만 성립)
map<int, int> m;
for (int i = (int)base1; i < max_v; i++)
for (auto a : adj_list[i]) m[a]++;
for (int i = (int)base1; i < max_v; i++)
if (m[i] == 0) { root = i; break; }
}
vi parent(max_v), level(max_v);
function<void(int, int, int)> dfs = [&](int node, int par, int lv) {
parent[node] = par, level[node] = lv;
for (auto adj : adj_list[node]) {
if (adj == par) continue; // 부모방향 간선제거
tree[node].push_back(adj);
dfs(adj, node, lv + 1);
}
};
dfs(root, -INF, 1);
return { tree, root, parent, level };
}
int32_t main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin>>T;while(T--){
int N;cin>>N;
vvi adj_list(N + 1);
REP(i,N - 1)
{
int u,v;cin>>u>>v; adj_list[u].push_back(v);
}
result r = treefy(adj_list, -1, true);
int a,b;cin>>a>>b;
if (r.level[a] > r.level[b]) swap(a, b);
while (r.level[a] < r.level[b]) b = r.parent[b];
while (a != b) a = r.parent[a], b = r.parent[b];
cout << (a) << '\n';
}
return 0;
}
|
cs |
$O(log N)$ 구현 (쿼리 형태면 $O(M \times log N)$)
역시 여기를 보면 과정을 이해하기 쉽도록 설명되어 있다. 다만, 그 구현과정에서 실수가 들어가기 쉬운데, 이건 내가 직접 구현하기보다 구현되어 있는걸 가져다 쓰는 형식이 좋을 것 같다. 내 라이브러리와 합쳐진 버전은 다음과 같다.
이 문제의 답안이기도 하다.
|
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
|
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
using vvi = vector<vi>;
const int INF = (int)1e9;
//무방향그래프를 인풋으로 받아, 트리로 만들어준다.LCA를 위한 작업도 해준다.
struct result {vvi tree;int root;vi parent,level; int max_level;vvi ancestor;};
result treefy(vector<vi>& adj_list, int root = -1, bool base1 = false) {
int max_v = (int)adj_list.size();
vector<vi> tree(max_v);
if (root == -1)
{
// find root(왼쪽이 parent인경우만 성립하는듯, 안그러면 root가 여러개다)
map<int, int> m;
for (int i = (int)base1; i < max_v; i++)
for (auto a : adj_list[i]) m[a]++;
for (int i = (int)base1; i < max_v; i++)
if (m[i] == 0) { root = i; break; }
}
vi parent(max_v), level(max_v);
int max_level = (int)floor(log2(max_v));
// ac[y][x] :: x의 2^y번째 조상을 의미
vvi ac(max_level + 1, vi(max_v, -INF));
function<void(int, int, int)> dfs = [&](int node, int par, int lv) {
parent[node] = par, level[node] = lv;
ac[0][node] = par; // 첫번째(2^0) 조상은 부모
for (auto adj : adj_list[node]) {
if (adj == par) continue; // 부모방향 간선제거
tree[node].push_back(adj);
dfs(adj, node, lv + 1);
}
};
dfs(root, -INF, 1);
for (int i = 1; i <= max_level; i++) {
for (int j = 1; j < max_v; j++) {
int tmp = ac[i - 1][j];
if (tmp == -INF) continue;
ac[i][j] = ac[i - 1][tmp];
}
}
return { tree, root, parent, level,max_level, ac };
}
int lca(int u, int v, int max_level, vi& level, vvi& ancestor)
{
if (level[u] < level[v]) swap(u, v); // b가 더 깊도록 조정
int diff = level[u] - level[v];
for (int i = 0; diff; i++) {
if (diff & 1) u = ancestor[i][u]; //b를 올려서 level을 맞춘다.
diff >>= 1;
}
if (u == v) return u;
for (int i = max_level; i >= 0; i--) {
// a와 b가 다르다면 현재 깊이가 같으니, 깊이를 a,b동시에 계속 올려준다.
if (ancestor[i][u] != ancestor[i][v])
u = ancestor[i][u], v = ancestor[i][v];
}
return ancestor[0][u];
}
int32_t main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int N; cin >> N; vvi adj_list(N + 1);
for (int i = 0; i < N - 1; i++) {
int u, v; cin >> u >> v;
adj_list[u].push_back(v);
adj_list[v].push_back(u);
}
result r = treefy(adj_list, 1, true);
int m; cin >> m; for (int z = 0; z < m; z++)
{
int a, b; cin >> a >> b;
int ans = lca(a, b, r.max_level, r.level, r.ancestor);
cout << (ans) << '\n';
}
return 0;
}
|
cs |
반응형
'Programming > Algorithm' 카테고리의 다른 글
| 강한연결요소(SCC, Strongly Connected Component), 코사라주, 타잔 (0) | 2020.05.03 |
|---|---|
| 스파스 테이블(Sparse Table), Binary Lifting (0) | 2020.05.01 |
| 위상정렬(Topological Sort) (0) | 2020.04.23 |
| 트라이(Trie) (0) | 2020.04.22 |
| KMP( Knuth, Morris, Prett) (0) | 2020.04.20 |
