# 110 選手班 - 樹論 ###### tags: `宜中資訊` `CP` Ccucumber12 2021.08.09 --- ## Outline - Diameter - Centroid - Euler Tour Tree - Lowest Common Ancestor - Heavy-Light Decomposition --- ## Diameter ---- ### Problem - Given A Tree $G$ - Find the longest path on $G$ - Time Complexity $O(N)$ ---- ### Solution I - 1. DFS on abitrary point and find the farthest point $S$ - 2. DFS on $S$ and find the farthest point $T$ - $path(S,T)$ is the diameter - two DFS $\rightarrow O(N)$ ---- #### Proof - let diameter be $\delta(S,T)$ - discuss start point $x$ in two situations: - $x$ in $\delta(S,T)$ - $x$ not in $\delta(S,T)$ ---- $x$ in $\delta(S,T)$ - if farthest point $z$ in 1.DFS is not $S$ or $T$ - $\implies \delta(x,z) > \delta(x,S)$ - $\implies \delta(x,z) + \delta(x,T) > \delta(x,S) + \delta(x,T)$ - $\implies \delta(z,T) > \delta(S,T) \Rightarrow\Leftarrow$ ---- $x$ not in $\delta(S,T)$ for farthest point $z$ in 1. DFS, discuss two situations: - $\delta(x,z)$ cross $\delta(S,T)$ - $\delta(x,z)$ not cross $\delta(S,T)$ ---- $\delta(x,z)$ cross $\delta(S,T)$ at $y$ - $\because z$ is farthest point and not $S$ or $T$ - $\therefore \delta(y,z) > \delta(y,S)$ - $\implies \delta(y,z) + \delta(y,T) > \delta(y,S) + \delta(y, T)$ - $\implies \delta(z,T) > \delta(S,T) \Rightarrow\Leftarrow$ ---- $\delta(x,z)$ not cross $\delta(S,T)$ - let $y$ be first point from $x$ to $S$ in $\delta(S,T)$ - let $y'$ be the first point from $y$ to $z$ in $\delta(x,z)$ - $\because \delta(S,T)$ is diameter and $z$ is not $S$ or $T$ - $\therefore \delta(y,S) \geq \delta(y,z)$ - $\implies \delta(y',y) + \delta(y,S) > \delta(y', z) \Rightarrow\Leftarrow$ ---- - $z$ has to be $S$ or $T$ - $2$. DFS must find the other endpoint by definition ---- #### Implementation ```c= #include <bits/stdc++.h> using namespace std ; vector<int> g[100010] ; int lev[100010], tar ; void dfs(int x, int p) { lev[x] = lev[p] + 1 ; if(lev[x] > lev[tar]) tar = x ; for(auto i:g[x]) if(i != p) dfs(i, x) ; } int main() { int n; // input g for(int i=1; i<=n; ++i) lev[i] = 0 ; dfs(1, 0) ; for(int i=1; i<=n; ++i) lev[i] = 0 ; int S = tar ; dfs(S, 0) ; int T = tar ; cout << S << ' ' << T << endl ; } ``` ---- ### Solution II - DP - find an arbitrary vertex as root - maintain $d_1$ and $d_2$ for every vertex - $d_1$: farthest descendent - $d_2$: second farthest descendent - $\delta(S,T) = d_1[LCA(S,T)] + d_2[LCA(S,T)]$ ---- - $\forall i$ is $x$ child: - $d_1[x] = max(d_1[i] + 1)$ - $d_2[x] = max(d_2[i] + 1, d_1[i] + 1$ where $d_1[i] + 1$ is not $d_1[x])$ - done by DFS + DP - $O(N)$ ---- ### Advance - Negative edge - Second Diameter --- ## Centroid ---- ### Problem - Given a tree $G$ - find the point $g$ S.T. - $max(|g \rightarrow subtree|)$ is smallest ---- ### Properties - at most two possible $g$, and they are adjacent - $max(|g \rightarrow subtree|) \leq |G|/2$ - $\sum(\delta(i,g))$ is smallest - if combine two trees, the new $g'$ will be in $\delta(g_1, g_2)$ - remove/add a new vertex, $g$ will move at most one edge ---- ### Solution - By property 1 and 2 - DFS through the tree - calculate $max(|x \rightarrow subtree|)$ - $O(N)$ ---- - $max(|x \rightarrow subtree|)=$ - $\forall i$ is $x$ child: $max(|subtree(i)|)$ - $|G| - \sum|subtree(i)| - 1$ - below and above ---- ### Implementation ```c= vector<int> g[100010] ; int sz[100010], n; vector<int> cen ; void dfs(int x, int p) { sz[x] = 1 ; int ret = 0 ; for(auto i:g[x]) if(i != p) { dfs(i, x) ; sz[x] += sz[i] ; ret = max(ret, sz[i]) ; } ret = max(ret, n - sz[x]) ; if(ret * 2 <= n) cen.push_back(x) ; } ``` --- ## Euler Tour Tree ---- ![](https://i.imgur.com/TRk1mbr.png) ---- ### Solution - DFS on a rooted tree - mark the nodes when enters and leaves - $O(N)$ ---- ### Properties - Every subtree is a interval - Turn tree problem into sequence problems - Use data structures to optimize ---- ### Application - LCA $\Rightarrow$ RMQ on Euler sequence - Add a subtree $\Rightarrow$ Insert an interval - Remove a subtree $\Rightarrow$ Remove an interval - Change root $\Rightarrow$ Rotate Euler sequence ---- ### Implementation ```c= vector<int> g[100010] ; vector<int> et ; int in[100010], out[100010] ; void dfs(int x, int p) { et.push_back(x) ; in[x] = et.size() - 1 ; for(auto i:g[x]) if(i != p) { dfs(i, x) ; et.push_back(x) ; } out[x] = et.size() - 1 ; } ``` ---- ### Euler Tour on Edge ![](https://i.imgur.com/WFJYSho.png) ---- - DFS on rooted tree - downwards: positive - upwards: negative - $O(E)$ ---- - calculate $path(S,T)$ - let $x=LCA(S,T)$ - $path(S,T)=[x,S] + [x,T]$ - prefix sum --- ## Lowest Common Ancestor ---- ### Problem - Given a rooted tree $G$ - find the lowest common root of $u$, $v$ - $O(log\ N)$ / query ---- ### Naive - going up at the lower point - until they meet each other - $O(h)$ ---- ### Solution I - Reduce the time to jump upwards - Binary lifting (Sparse table) - maintain the $2^i$ ancestor of each point ---- #### Build - $lca[x][i] = x \rightarrow 2^i\ ancestor$ - $lca[x][0] = fa[x]$ - $lca[x][i] = lca[lca[x][i-1]][i-1]$ ---- - DFS: $O(N)$ - DP: $O(NlogN)$ - Total: $O(NlogN)$ ---- #### LCA 1. Move lower one to the same level - let $y$ be the depth difference - convert $y$ into binary - jump through $lca[x][i]$ 2. Binary Search the highest point where $u$, $v$ don't meet 3. Father of that point is $LCA(u,v)$ ---- - Move to same level: $O(log\ N)$ - Binary Search: $O(log\ N)$ - Total: $O(log\ N)$ ---- #### Implementation ```c= vector<int> g[100010] ; int lca[30][100010] ; int lev[100010] ; void dfs(int x, int p) { lca[0][x] = p ; lev[x] = lev[p] + 1 ; for(auto i:g[x]) if(i != p) dfs(i, x) ; } void build(int n) { for(int i=1; i<=log2(n); ++i) for(int j=1; j<=n; ++j) lca[i][j] = lca[i-1][lca[i-1][j]] ; } int query(int a, int b) { if(lev[a] < lev[b]) swap(a, b) ; for(int i=25; i>=0; --i) if(lev[lca[i][a]] >= lev[b]) a = lca[i][a] ; if(a == b) return a ; for(int i=25; i>=0; --i) { if(lca[i][a] != lca[i][b]) { a = lca[i][a] ; b = lca[i][b] ; } } return lca[0][a] ; } ``` ---- ### Solution II - Do Euler Tour Tree - maintain $pos(x)$ which is the first time $x$ appears in Euler sequence - $LCA(u,v)=min[pos(u), pos(v)]$ ---- - Euler Tour: $O(N)$ - RMQ: Sparse Table - Build: $O(NlogN)$ - Query: $O(1)$ --- ## Heavy-Light Decomposition ---- ### Problem - Path problems on Trees - with modify and query - ex. dynamic path sum/max/min ---- ### Solution - Separate the tree into sequences to fit in DS - Time Complexity: $O($sequences$) \times O(DS)$ - $\implies$ minimum sequence for every path - $\implies$ Heavy-Light Decomposition (HLD) ---- - Heavy Edge: edge to max subtree - Light Edge: Every other edges - Connect vertex through Heavy Edges - $\implies$ every time switch a sequence, the size multiplies by two at least - $\implies$ switch at most $O(log\ N)$ sequences - Time complexity: $O(log\ N) \times O(DS)$ ---- ![](https://i.imgur.com/mhNSfp4.png) ---- ### Implementation ```c= const int MXN = 200000; const int N = MXN + 10; struct info{ int w; int dep, pa, size, next; int id, root; // id on data structure } inf[N]; vec<int> g[N]; int prepare(int v, int pa, int d=0){ // find : dep, pa, size, next int maxsub = 0; inf[v].pa = pa; inf[v].dep = d; inf[v].size = 1; inf[v].next = -1; for(auto i:g[v]){ if(i == pa) continue; int sub = prepare(i, v, d+1); inf[v].size += sub; if(sub > maxsub) { maxsub = sub; inf[v].next = i; } } return inf[v].size; } int cntID = 1; void mapTo(int v, int pa, int root){ // map : id, root inf[v].id = cntID++; inf[v].root = root; modify(inf[v].id, inf[v].w); if(inf[v].next != -1) mapTo(inf[v].next, v, root); for(auto i:g[v]){ if(i == pa) continue; if(i == inf[v].next) continue; mapTo(i, v, i); } } void update(int x, int v){ inf[x].w = v; modify(inf[x].id, inf[x].w); } int query(int s, int t){ int ans = 0; while(inf[s].root != inf[t].root){ if(inf[inf[s].root].dep < inf[inf[t].root].dep) swap(s, t); amax(ans, DSquery(inf[s].id, inf[inf[s].root].id)); s = inf[inf[s].root].pa; } amax(ans, DSquery(inf[s].id, inf[t].id)); return ans; } ``` --- ## Credit - 演算法筆記 - OI wiki - https://www.chegg.com/homework-help/questions-and-answers/euler-tour-graph-path-traverses-edge-exactly--context-tree-say-edge-bidirectional-euler-to-q60792427
{"metaMigratedAt":"2023-06-16T06:20:36.615Z","metaMigratedFrom":"Content","title":"110 選手班 - 樹論","breaks":true,"contributors":"[{\"id\":\"45262441-89e4-46ca-959b-c0d6fadb64e2\",\"add\":9309,\"del\":380}]"}
    299 views