POJ 1330

POJ 1330

題敘

https://vjudge.net/problem/POJ-1330
LCA 模板題
給一棵樹,有 $T$ 筆詢問,求點 $u$ 與 $v$ 的最低共同祖先

想法

使用倍增法爬行
先將兩點維持到相同的深度
如果兩點相同,則這個點就是答案
否則爬行直到兩者的祖先是相同的

Code

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
85
86
87
88
89
//By Koios1143
#include<iostream>
#include<vector>
#include<cmath>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define pii pair<int,int>
using namespace std;
int t,n,l,r,root,lgN;
int P[10010][100],D[10010],from[10010];
vector<int> nxt[10010];
//Parents
void computeP(){
for(int i=0 ; i<lgN ; i++){
for(int j=1 ; j<=n ; j++){
if(P[j][i] == -1) P[j][i+1] = -1;
else P[j][i+1] = P[P[j][i]][i];
}
}
}

bool vis[10010];
void computeD(int px,int h){
D[px]=h;
vis[px]=true;
for(int i=0 ; i<nxt[px].size() ; i++){
if(!vis[nxt[px][i]])
computeD(nxt[px][i],h+1);
}
}

//Distance
int LCA(int u,int v){
if(D[u]>D[v])
swap(u,v);
int dis=D[v]-D[u];
for(int i=lgN ; i>=0 ; i--){
if(dis&(1<<i)) v=P[v][i];
}
if(u==v) return v;
for(int i=lgN ; i>=0 ; i--){
if(P[u][i] != P[v][i]){
u=P[u][i];
v=P[v][i];
}
}
return P[v][0];
}
int logbase(double a, double base){
return log(a) / log(base);
}
void init(){
lgN=logbase(n,2);
for(int i=0 ; i<10010 ; i++){
for(int j=0 ; j<lgN ; j++){
P[i][j]=-1;
}
D[i]=0;
from[i]=-1;
vis[i]=false;
nxt[i].clear();
}
}

int main(){
IOS;
cin>>t;
while(t--){
cin>>n;
init();
for(int i=0,x,y ; i<n-1 ; i++){
cin>>x>>y;
P[y][0]=x;
from[y]=x;
nxt[x].push_back(y);
}
for(int i=1 ; i<=n ; i++){
if(from[i]==-1){
root=i;
break;
}
}
computeP();
computeD(root,0);
cin>>l>>r;
cout<<LCA(l,r)<<"\n";
}
return 0;
}

複雜度

預處理時間複雜度 $O(NlogN + N)$
每筆詢問時間複雜度 $O(NlogN)$