樹上倍增的經典應用是求兩個節點的LCAphp
固然它的做用不只限於求LCA,還能夠維護節點的不少信息html
求LCA的方法除了倍增以外,還有樹鏈剖分、離線tarjan ,這兩種往後再講(衆人:實際上是你不會吧:unamused:。。。)node
樹上倍增嘛,顧名思義就是倍增算法
相信倍增你們都不默認,著名的rmq問題的$O(n*logn)$的解法就是利用倍增實現的數組
在樹上倍增中,咱們用post
$f[j][i]$表示第$j$號節點,跳了$2^j$步所能到達的節點url
$deep[i]$表示$i$號節點的深度spa
而後用這兩個數組瞎搞搞就能整出LCA來啦code
衆人::wrench: :hammer: :hocho:htm
首先,$f[i][0]$(也就是一個節點的上面的節點)容易求得,只要對整棵樹進行一邊dfs就好,在dfs的時候咱們順即可以求出$deep$數組
for(int i=head[now];i!=-1;i=edge[i].nxt) if(!deep[edge[i].v]) deep[edge[i].v]=deep[now]+1,f[edge[i].v][0]=now,dfs(edge[i].v);
這段代碼應該不難理解
那麼咱們怎麼維護$f$數組呢?
不可貴到$f[j][i]=f[f[j][i-1]][i-1]$ 衆人:難!
其實真的不難,一張圖就能夠解釋明白啦
這句話的意思實際上是說,一個節點跳$2^j$所能到達的節點其實是跳$2^{i-1}$所能到達的節點再往上跳$2^{j-1}$步
注意$2^i=2^{i-1}+2^{i-1}$
代碼:
for(int i=1;i<=19;i++) for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];
接下來要進入最核心的部分啦,
咱們如何用$deep$和$f$亂搞搞出$x$和$y$的LCA呢?
按照書上倍增算法的介紹
咱們求LCA須要分爲兩步
設$deep[x]>deep[y]$
根據二進制分解什麼亂七八糟的,這麼作必定是對的,其實這個挺顯然的,yy一下就行了吧。。。
if(deep[x]<deep[y]) swap(x,y); for(int i=19;i>=0;i--) if(deep[f[x][i]]>=deep[y]) x=f[x][i];
首先處理一下$x$和$y$的深度,保證$deep[x]>deep[y]$
而後儘可能讓$x$向上跳就好啦,注意這裏是能夠取到等號的
這個時候他們的最近公共祖先就是$y$
if(x==y) return x;
同時向上跳,直到祖先相同爲止
那麼此時他們再向上跳一步所能到達的節點就是LCA啦
for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
怎麼樣?
是否是很簡單?
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 1000010; inline void read(int &n) { char c = getchar(); bool flag = 0; n = 0; while (c < '0' || c > '9') c == '-' ? flag = 1, c = getchar() : c = getchar(); while (c >= '0' && c <= '9') n = n * 10 + c - 48, c = getchar(); flag == 1 ? n = -n : n = n; } struct node { int v, nxt; } edge[MAXN]; int head[MAXN]; int num = 1; inline void add_edge(int x, int y) { edge[num].v = y; edge[num].nxt = head[x]; head[x] = num++; } int f[MAXN][21]; int deep[MAXN]; int n, m, root; void dfs(int now) { for (int i = head[now]; i != -1; i = edge[i].nxt) if (!deep[edge[i].v]) deep[edge[i].v] = deep[now] + 1, f[edge[i].v][0] = now, dfs(edge[i].v); } void PRE() { for (int i = 1; i <= 19; i++) for (int j = 1; j <= n; j++) f[j][i] = f[f[j][i - 1]][i - 1]; } int LCA(int x, int y) { if (deep[x] < deep[y]) swap(x, y); for (int i = 19; i >= 0; i--) if (deep[f[x][i]] >= deep[y]) x = f[x][i]; if (x == y) return x; for (int i = 19; i >= 0; i--) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; return f[x][0]; } int main() { memset(head, -1, sizeof(head)); read(n); read(m); read(root); for (int i = 1; i <= n - 1; i++) { int x, y; read(x); read(y); add_edge(x, y); add_edge(y, x); } deep[root] = 1; dfs(root); PRE(); for (int i = 1; i <= m; i++) { int x, y; read(x); read(y); printf("%d\n", LCA(x, y)); } return 0; }
都是些入門難度的題目
http://www.cnblogs.com/zwfymqz/p/6832524.html
http://www.cnblogs.com/zwfymqz/p/7791527.html
http://www.cnblogs.com/zwfymqz/p/7791617.html
http://www.cnblogs.com/zwfymqz/p/7791517.html