倍增小結 ST 與 LCA

2022-04-10 03:17:28 字數 3250 閱讀 4246

倍增我是真滴不會

倍增法(英語:binary lifting),顧名思義就是翻倍。

能夠使線性的處理轉化為對數級的處理,大大地優化時間複雜度。

(ps:上次學倍增lca,沒學會,老老實實為了嚴格次小生成樹滾回來重新學)

st表\(n~log(n)~\)的預處理與\(o~(1)\)的查詢

感性理解一下

對於每次詢問\([l,~r]\)

q:這裡為什麼不能直接用\(f_\) 呢?

a:因為我們這裡的\(log\)是向下取整的,可能會出現有一塊取不到的部分

q:那有重複的部分怎麼辦吶??

a:重複部分對答案的貢獻有影響嗎?

q:貌似莫得影響

a:\(ans = max(f_, f_)\)

注意點

\[log_2 1 = 0

\]\[log_2 x = log_2 \frac + 1

\]第二個式子是這樣推導出來的

\[log_2~x = log_2~2\times\frac\\

~~~~~~~~~~~~~~~= log_2 + log_2\frac\\

~~~~~~~~~=1+log_2\frac

\]code

#include #include #include #include #include #define ll long long

using namespace std;

const int logn = 22;

const int n = 2000001;

int read()

int log[n];

int n ,m;

void pre()

int f[n][25];

int main()

system("pause");

return 0;

}

倍增求lca

例題可以每次找深度比較大的那個點,讓它向上跳。顯然在樹上,這兩個點最後一定會相遇,相遇的位置就是想要求的 lca。 或者先向上調整深度較大的點,令他們深度相同,然後再共同向上跳轉,最後也一定會相遇.(摘自oi wiki)

就是從深度深的向上跳,到達同一深度後一起向上跳(學過樹剖的都知道吧,艹估計沒人跟我一樣先學的樹剖,然後回來學倍增)

倍增就很優秀了

dfs

void dfs(int x, int fa) 

}

這一塊可以解釋一下,假如說求的是最下面兩個節點,很明顯兩個點的lca是紫線所指向的點,問題是怎麼找,\(\text\) 迴圈從大的向小遍歷,可以發現先從\(紅線 \to 粉線 \to 藍線 \to\)到了藍線所指的點,會向上跳一次到了紫線就發現了lca

\(\text \) 就是答案

這種是較為樸素倍增的求法,沒有用\(log\)優化

加入log優化的(但好像沒太大的常數優化)

log陣列還是原來的求法

void dfs(int x, int fa)
有一說一,我不大喜歡這種優化的方法我最喜歡的還是樹剖

#include #include using namespace std;

const int n = 5e5 + 10;

struct tree e[n << 1];

int nume, head[n];

void add_edge(int from, int to)

int dep[n], f[n][21];

void dfs(int u, int fa)

}int lca(int x, int y)

if (x == y)

return x;

for (int i = 20; i >= 0; i--)

return f[x][0];

}int main()

dfs(s, 0);

for (int i = 1, x, y; i <= m; i++)

}

#include #include #include #include #include #define ll long long

using namespace std;

const int n = 5e5 + 10;

int read()

struct edge e[n << 1];

int head[n], nume;

void add_edge(int from, int to)

int f[n][25], log[n], dep[n];

void dfs(int x, int fa)

}int lca(int x, int y)

return f[x][0];

}int main()

log[1] = 0;

for (int i = 2; i <= n; i++)

log[i] = log[i / 2] + 1;

dfs(s, 0);

for (int i = 1; i <= m; i++)

system("pause");

return 0;

}

#include #include using namespace std;

const int n = 5e5 + 10;

int head[n], nume;

struct node e[n << 1];

void add_edge(int from, int to)

int fath[n], siz[n], dep[n], son[n];

void dfs(int x, int fa)

}int dfn[n], pre[n], top[n], cnt;

void dfs2(int x, int tp)

}int lca(int x, int y)

if (dep[x] < dep[y])

swap(x, y);

return y;

}int n, m, s;

int main()

dfs(s, 0), dfs2(s, s);

for (int i = 1; i <= m; i++)

return 0;

}

LCA 倍增 學習小結

lca問題,即lowest common ancestor 最近公共祖先 lca問題有很多解法,比如 暴力解法 分塊解法 倍增演算法 tarjan演算法 lct演算法 在這裡呢,我們只討論倍增 倍增演算法其實是一種類似於dp的實現 pa i j p a i j 表示i號節點的2j 2 j個父親 pa...

倍增法與st

一篇部落格 最常用,也是最簡單的演算法,實質就是直接對暴力使用倍增優化將複雜度降低達到需求。有樹上的倍增和區間的倍增 depth 為每個節點的深度,fa i j i節點的 2 j 的父親。lg i log 2 1 const int maxn 5000001 int depth maxn fa ma...

倍增LCA複習

時間過去了如此之久,我連倍增lca都不怎麼記得了,要粗事啊。首先預處理層數和每個節點的父親,然後預處理p陣列,p i,j 表示i向上第2 j個祖先。最後對於每個詢問x,y先把x,y變成同一層數的 x或y向上走直到兩個層數相等 然後x,y同時向上走,直到x和y的父親相同位置。自 1.dfs預處理出所有...