LCA === ###### tags: `Algorithm` ## 提示 * DFS : 建表 $2^n$ 輩祖先, 紀錄時間戳(用來判祖先) ```cpp void DFS( int u, int p, int d) { tin[u] = t++; anc[u][0] = p; used[u][0] = time; for( int i=1; 1<<i < d; i++) { anc[u][i] = anc[ anc[u][i-1] ][i-1]; //used[u][i] = time; } for( vector<int>::iterator v=adj[u].begin(); v!=adj[u].end(); v++) { DFS( *v, u, d+1); } tout[u] = t++; } ``` * 判祖先 ```cpp inline bool isAnc( int a, int b) { if (a<=0) return 1; // 祖先不存在(爬過頭) return tin[a] < tin[b] && tout[b] < tout[a]; } ``` * 由一點 a 往上爬 如果成功(要爬的點是b的祖先)就改爬一半試看看 如果失敗(要爬的點不是b的祖先), 就可以爬過去 目標是爬到成功的前一個, 則 LCA 是他的父親 ```cpp int LCA( int a, int b) { if (a==b || isAnc(a,b)) return a; if (isAnc(b,a)) return b; for( int i=logN; i>=0; i--) { if (!(isAnc(anc[a][i],b)) //|| used[a][i]<time)) { a = anc[a][i]; } } return anc[a][0]; } ``` :::danger 一般倍增法的步數是大於0 但 lca 是步數大於等於 0 因爲就算是 0 實際上步數是 1 ::: ## POJ1330 ```cpp #include<iostream> #include<vector> #include<string.h> using namespace std; #define maxN 10010 #define logN 15 int N; int anc[maxN][logN+10], used[maxN][logN+10], time; vector<int> adj[maxN]; int tin[maxN], tout[maxN], t; void DFS( int u, int p, int d) { tin[u] = t++; anc[u][0] = p; used[u][0] = time; for( int i=1; 1<<i < d; i++) { anc[u][i] = anc[ anc[u][i-1] ][i-1]; used[u][i] = time; } for( vector<int>::iterator v=adj[u].begin(); v!=adj[u].end(); v++) { DFS( *v, u, d+1); } tout[u] = t++; } inline bool isAnc( int a, int b) { if (a<=0) return 1; return tin[a] < tin[b] && tout[b] < tout[a]; } int LCA( int a, int b) { if (a==b || isAnc(a,b)) return a; if (isAnc(b,a)) return b; for( int i=logN; i>=0; i--) { if (!(isAnc(anc[a][i],b) || used[a][i]<time)) { a = anc[a][i]; } } return anc[a][0]; } int main() { ios::sync_with_stdio(0); cin.tie(0); int T; cin >> T; for( time=1; time<=T; time++) { t=0; for( int i=0; i<maxN; i++) { adj[i].clear(); adj[i].reserve(100); } cin >> N; long long rt = ((1+N)*N)>>1; for( int i=0; i<N-1; i++) { int a, b; cin >> a >> b; adj[a].push_back(b); rt-=b; } DFS( rt, -1, 0); int a, b; cin >> a >> b; cout << LCA(a,b) << '\n'; } } ```