# LCA ## 樹壓平 把Dfs時遍歷的節點的順序都記錄下來,就會產生一串字元,例如: ``` A / \ B E / \ \ C D F dfs順序:ABCBDBAEFEA ``` 這樣就叫做樹壓平,在dfs中順便紀錄每個節點的深度,再帶回dfs順序就會得到一串數字 ``` dfs順序:ABCBDBAEFEA depth :01212101210 ``` 當我們要找lca(u,v)時,先找到dfs順序中最早出現的u的位置i,和dfs順序中最早出現的v的位置j,在區間[i,j]中找誰的depth有最小值,該點就是(u,v)的lca,例如: ``` A / \ B E / \ \ C D F dfs順序:ABCBDBAEFEA depth :01212101210 (1) 假設要找lca(C,D): 區間:CBD 212 1為最小值,所以1對應到的B就是lca(C,D); (2) 假設要找lca(C,E): 區間:CBDBAE 212101 0為最小值,所以0對應到的A就是lca(C,E); ``` 那麼LCA問題就變成QRM問題了,可用[Segment Tree](/nrT8D7eqSUWZYrTEj3SjNQ),[Sparse Table](/jFKC0KV0S-6iQiPaao9Bcg)等資結來完成 :::spoiler ST Code ```cpp= #include <bits/stdc++.h> #define int long long using namespace std; #define pii pair<int,int> #define F first #define S second #define pb push_back #define all(a) (a).begin(),(a).end() #define mem(a) memset(a,0,sizeof(a)) #define inf INT_MAX #define mod 1000000007 #define MAXN 100005 int n,m,x,y,q,ans; //----------------------------------- vector<int> child[MAXN]; int fa[MAXN],node_idx[MAXN],node_depth[MAXN],idx; pii st[MAXN][20]; void built(){ for(int j=1;j<=log2(idx);j++){ for(int i=0;i+(1<<j)-1<=idx;i++){ st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } } } pii query(int l,int r){ int k=log2(r-l+1); return min(st[l][k],st[r-(1<<k)+1][k]); } void dfs(int now,int depth){ node_depth[now]=depth; node_idx[now]=idx; st[idx][0].F=depth,st[idx++][0].S=now; for(auto i:child[now]){ dfs(i,depth+1); st[idx][0].F=depth,st[idx++][0].S=now; } } signed main(){ cin.sync_with_stdio(0),cin.tie(0); cin >> n >> q; for(int i=1;i<=n;i++){ while(cin >> x && x){ child[i].pb(x); fa[x]++; } } for(int i=1;i<=n;i++){ if(fa[i]==0){ dfs(i,0); break; } } built(); while(q--){ cin >> x >> y; int lb=node_idx[x],rb=node_idx[y]; if(lb>rb) swap(lb,rb); pii lca=query(lb,rb); int len=node_depth[x]-lca.F+node_depth[y]-lca.F; cout << lca.S << ' ' << len << '\n'; } } ``` ::: ## 樹鏈剖分HDL 將一棵樹分成很多條鏈,也就是一串由上到下的結點,我們要找到一條主幹道(重鏈),其餘為次幹道,在每次查詢時,兩個點的top不在同一條鏈的話,就要把深度較低的top的節點往上跳到該節點的father,重複此步驟,直到兩點的top相同,接著深度較淺的點(較上面的點)就是LCA :::spoiler HDL Code ```cpp= #include <bits/stdc++.h> using namespace std; #define pb push_back #define MAXN 30005 int N=1,n,m,x,k,a,b,q,ans; //----------------------------------- int fa[MAXN],wson[MAXN],top[MAXN],depth[MAXN]; vector<int> Map[MAXN],sz(MAXN,1); void dfs1(int now){ for(auto i:Map[now]){ fa[i]=now; //record i's father depth[i]=depth[now]+1; //record i's depth dfs1(i); sz[now]+=sz[i]; //record the number of subtree's node if(sz[i]>sz[wson[now]]) wson[now]=i; //find the weight son } } void dfs2(int now,int tp){ top[now]=tp; //record the top if(wson[now]) dfs2(wson[now],tp); //go to the weight son first for(auto i:Map[now]){ if(i==wson[now]) continue; dfs2(i,i); //go to the slight son second } } int query(int a,int b){ while(top[a]!=top[b]){ //until a and b in the same chain if(depth[top[a]]>depth[top[b]]){ //take deeper top of element to its top's father a=fa[top[a]]; }else{ b=fa[top[b]]; } } //return upper node,means the one has lower depth if(depth[a]<depth[b]) return a; else return b; } signed main(){ cin.sync_with_stdio(0),cin.tie(0); cin >> n >> q; vector<bool> root(n+1,true); //input for(int i=1;i<=n;i++){ while(cin >> x && x){ Map[i].pb(x); root[x]=false; } } //find the root int r=1; for(;r<=n;r++) if(root[r]) break; //built dfs1(r); dfs2(r,r); //query while(q--){ cin >> a >> b; int lca=query(a,b); cout << lca << ' ' << depth[a]+depth[b]-2*depth[lca] << '\n'; } } ``` :::