講解 模板 最近公共祖先(LCA)(倍增)

2021-08-10 01:53:32 字數 3566 閱讀 6037

閱讀須知:我認為讀者已經掌握(或了解)了:

倍增思想

樹(圖)的基本概念及簡單實現

存圖與建圖

dfs

嗯,我們來看看最近公共祖先(lca)的一種實現方式——倍增。

話說什麼是最近公共祖先呢?emmm……大家如果知道樹的話,應該就知道父親節點與兒子節點了吧,那麼祖先就是父親的父親的父親的……;總之在同一條樹鏈上,若x的深度小於y的深度,則稱x是y的祖先。公共祖先,顧名思義就是兩個點共同擁有的祖先;但是若x是y的祖先,則他們的共同祖先是x(很奇怪是不是)。最近公共祖先可以依字面意思理解,即「最近的」公共祖先。下面附圖說明~

如上圖4和7的公共祖先有5、10,最近公共祖先為5;

3與20的公共祖先和最近公共祖先都是10;

2和3的公共祖先是3、5、10,最近公共祖先是3。

怎麼樣,對最近公共祖先是不是有些了解了。

下面我們再來看兩張圖~

請讀者們仔細比對上面兩張圖,會發現,選擇不同的根節點會使得兩點之間有著不同的最近公共祖先。

關於實現lca,想必大家都有暴力的思路,不斷詢問x與y的父親節點,直到發現他們詢問到相同的父親節點位置,這當然會超時!所以,我們著查詢【滑稽】,這就是倍增的思想。

我們使用乙個陣列lst[i][j]表示從i這個點向上跳2^j次所對應的點,即節點i的第2^j個父親節點的編號。比如說上面第二個圖中lst[5][0]=2,lst[5][1]=1。

請讀者們細細體會下面的轉化【很重要】(可以畫一顆樹試一試):

lst[son][i + 1] = lst[ lst[son][i] ][i]

1.建圖(建議使用鏈式前向星)

2.預處理,更新lst陣列

3.查詢

下面分條作答

//建樹一般不用存邊權,建樹一般要存雙向邊

struct edge

edge[2 * max_data]; //雙向邊

int edge_size = 0; //前向星陣列模擬指標

int head[max_data]; //起點

void add(int u, int v, int w)

思想:利用上面說的重要式子更新lst。從根節點出發,遍歷每乙個點,在遍歷時利用父子關係【滑稽】更新lst。

注意:要使根節點深度設為1(否則會使後面的st可能變為-1)。

int vis[max_data];  //防止乙個點被遍歷多次【重要】

int lst[max_data][33]; //倍增表

int deep[max_data]; //記錄深度

int main()

void dfs(int x) //從根節點開始遍歷}}

也可以不把那個重要式子加進dfs裡,也可以dfs後單獨再建倍增表(st表)

void increase__pretreatment()

終於到倍增大顯身手的時候了,上面我們說到逐層查詢會很慢,所以我們跳著查,通過倍增法達到快速上搜的目的。

首先,我們找到乙個深度較大的點,利用倍增快速上搜到另乙個點相同的深度。

然後,讓兩個點同時倍增快速上搜,直到搜到他們最近公共祖先的兒子為止。

注意,倍增快速上蒐時從較大跨度逐漸向較小跨度搜,這樣可以保證搜尋到正確答案。

int lca(int x, int y)

題目描述

如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。

輸入格式:

第一行包含三個正整數n、m、s,分別表示樹的結點個數、詢問的個數和樹根結點的序號。

(資料保證可以構成樹)。

接下來m行每行包含兩個正整數a、b,表示詢問a結點和b結點的最近公共祖先。

輸出格式:

輸出包含m行,每行包含乙個正整數,依次為每乙個詢問的結果。 時空限制:1000ms,128m

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define by mashiro_ylb

#define time 2017\10\30

using

namespace

std;

const

int max_data = 500007;

struct edge

edge[2 * max_data];

int edge_size = 0;

int head[max_data];

int n, m, s;

int vis[max_data];

int lst[max_data][33];

int deep[max_data];

template

void read(t &x)

template

void write(t x)

void add(int u, int v)

template

t change(t &x, t &y)

void dfs(int s);

void lca();

void init();

void work();

int main()

void init()

deep[s] = 1;

dfs(s);

}void dfs(int x)

}}int lca(int x, int y)

void work()

}

嗯,還是比較簡單的一種演算法,大家加油吧。

!注意:本篇博文以模板為主,對倍增什麼的講解並不細緻,建議大家優先深入了解倍增(還有其他不細緻講解)的具體思想及其具體實現,謝謝。

2017.11.8 修改遺漏點(初始深度設為1)

2017.11.8 **優化——if(deep[x] - (1 << st) >= deep[y]) -> if(deep[lst[x][i]] >= deep[y])——

模板 LCA 最近公共祖先 倍增法

2019 11 07 09 25 45 c.樹之呼吸 叄之型 樹上兩點路徑長度 time limit 1000 ms memory limit 32768 k total submit 7 4 users total accepted 2 2 users special judge no descr...

LCA 最近公共祖先 (倍增演算法)

首先了解一下我們 最近公共祖先 e和g的lca為a l和j的lca為d k和f的lca為b 然後 倍增 用到了二進位制和 dp 的思想 倍增 就是 1 2 4 8 16 任何乙個數 都是可以右 這些數相加得到的。了解一下二進位制 首先 定義 fa i j 為 從 i 節點 向上走 2 j 個節點,d...

最近公共祖先 LCA 倍增演算法

樹上倍增求lca lca指的是最近公共祖先 least common ancestors 如下圖所示 4和5的lca就是2 那怎麼求呢?最粗暴的方法就是先dfs一次,處理出每個點的深度 然後把深度更深的那乙個點 4 乙個點地乙個點地往上跳,直到到某個點 3 和另外那個點 5 的深度一樣 然後兩個點一...