倍增法(英語:binary lifting),顧名思義就是翻倍。
可以使線性的處理轉化爲對數級的處理,大大地優化時間複雜度。
(ps:上次學倍增LCA,沒學會,老老實實爲了嚴格次小生成樹滾回來從新學)node
ST表ios
\(n~log(n)~\)的預處理與\(O~(1)\)的查詢git
感性理解一下
對於每次詢問\([l,~r]\)算法
Q:這裏爲何不能直接用\(f_{i,~x}\) 呢?數組
A:由於咱們這裏的\(log\)是向下取整的,可能會出現有一塊取不到的部分函數
Q:那有重複的部分怎麼辦吶??優化
A:重複部分對答案的貢獻有影響嗎?spa
Q:貌似莫得影響.net
A:
\(ans = max(f_{l,~x}, f_{r - 2^x + 1,~x})\)指針
注意點¶
輸入輸出數據通常不少,建議開啓輸入輸出優化。
每次用 std::log 從新計算 log 函數值並不值得,建議進行以下的預處理:
第二個式子是這樣推導出來的
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <string> #define ll long long using namespace std; const int logn = 22; const int N = 2000001; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int Log[N]; int n ,m; void pre() { Log[1] = 0; Log[2] = 1; for (int i = 3; i <= n; ++i) Log[i] = Log[i / 2] + 1; } int f[N][25]; int main() { n = read(), m = read(); for (int i = 1; i <= n; ++i) f[i][0] = read(); pre(); for (int j = 1; j <= logn; ++j) for (int i = 1; i + (1 << j) - 1 <= n; i++) f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); for (int i = 1; i <= m; i++) { int l = read(), r = read(); int log_x = Log[r - l + 1]; printf("%d\n", max(f[l][log_x], f[r - (1 << log_x) + 1][log_x])); } system("pause"); return 0; }
能夠每次找深度比較大的那個點,讓它向上跳。顯然在樹上,這兩個點最後必定會相遇,相遇的位置就是想要求的 LCA。 或者先向上調整深度較大的點,令他們深度相同,而後再共同向上跳轉,最後也必定會相遇.(摘自OI Wiki)
就是從深度深的向上跳,到達同一深度後一塊兒向上跳(學過樹剖的都知道吧,艹估計沒人跟我同樣先學的樹剖,而後回來學倍增)
倍增就很優秀了
void dfs(int x, int fa) { dep[x] = dep[fa] + 1; f[x][0] = fa; for (int i = 1; (1 << i) <= dep[x]; i++) f[x][i] = f[f[x][i - 1]][i - 1]; for (int i = head[x]; i; i = e[i].next) { int to = e[i].to; if (to != fa) dfs(to, x); } }
這種是較爲樸素倍增的求法,沒有用\(log\)優化
Lo數組仍是原來的求法
void dfs(int x, int fa) { f[x][0] = fa; dep[x] = dep[fa] + 1; for (int i = 1; i <=Log[dep[x]]; ++i) f[x][i] =f[f[x][i - 1]][i - 1]; for (int i = head[x]; i; i = e[i].net) if (e[i].to != fa) dfs(e[i].to, x); }
有一說一,我不大喜歡這種優化的方法我最喜歡的仍是樹剖
#include <cstdio> #include <iostream> using namespace std; const int N = 5e5 + 10; struct tree { int from, to, next; } e[N << 1]; int nume, head[N]; void add_edge(int from, int to) { e[++nume].from = from; e[nume].to = to; e[nume].next = head[from]; head[from] = nume; } int dep[N], f[N][21]; void dfs(int u, int fa) { dep[u] = dep[fa] + 1; f[u][0] = fa; for (int i = 1; (1 << i) <= dep[u]; i++) f[u][i] = f[f[u][i - 1]][i - 1]; for (int i = head[u]; i; i = e[i].next) { int to = e[i].to; if (to != fa) dfs(to, u); } } int lca(int x, int y) { if (dep[x] > dep[y]) swap(x, y); for (int i = 20; i >= 0; i--) { if (dep[x] <= dep[y] - (1 << i)) y = f[y][i]; } if (x == y) return x; for (int i = 20; i >= 0; i--) { if (f[x][i] == f[y][i]) continue; x = f[x][i], y = f[y][i]; } return f[x][0]; } int main() { int n, m, s; scanf("%d%d%d", &n, &m, &s); for (int i = 1, x, y; i < n; i++) { scanf("%d%d", &x, &y); add_edge(x, y); add_edge(y, x); } dfs(s, 0); for (int i = 1, x, y; i <= m; i++) { scanf("%d%d", &x, &y); printf("%d\n", lca(x, y)); } }
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <string> #define ll long long using namespace std; const int N = 5e5 + 10; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } struct Edge { int from, to, net; } e[N << 1]; int head[N], nume; void add_edge(int from, int to) { e[++nume].from = from, e[nume].to = to, e[nume].net = head[from], head[from] = nume; } int f[N][25], Log[N], dep[N]; void dfs(int x, int fa) { f[x][0] = fa, dep[x] = dep[fa] + 1; for (int i = 1; i <= Log[dep[x]]; i++) f[x][i] = f[f[x][i - 1]][i - 1]; for (int i = head[x]; i; i = e[i].net) { int to = e[i].to; if (to == fa) continue; dfs(to, x); } } int lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); while (dep[x] > dep[y]) x = f[x][Log[dep[x] - dep[y]]]; if (x == y) return x; for (int i = Log[dep[x]] ; i >= 0; i--) { if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; } return f[x][0]; } int main() { int n = read(), m = read(), s = read(); for (int i = 1, u, v; i < n; i++) { u = read(), v = read(); add_edge(u, v), add_edge(v, u); } 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++) { int x = read(), y = read(); printf("%d\n", lca(x, y)); } system("pause"); return 0; }
#include <cstdio> #include <iostream> using namespace std; const int N = 5e5 + 10; int head[N], nume; struct node { int from, to, next; } e[N << 1]; void add_edge(int from, int to) { e[++nume].from = from; e[nume].to = to; e[nume].next = head[from]; head[from] = nume; } int fath[N], siz[N], dep[N], son[N]; void dfs(int x, int fa) { siz[x] = 1; fath[x] = fa, dep[x] = dep[fa] + 1; for (int i = head[x]; i; i = e[i].next) { int to = e[i].to; if (to == fa) continue; dfs(to, x), siz[x] += siz[to]; if (siz[son[x]] < siz[to]) son[x] = to; } } int dfn[N], pre[N], top[N], cnt; void dfs2(int x, int tp) { dfn[x] = ++cnt, pre[cnt] = x, top[x] = tp; if (son[x]) dfs2(son[x], tp); for (int i = head[x]; i; i = e[i].next) { int to = e[i].to; if (to == fath[x] || to == son[x]) continue; dfs2(to, to); } } int lca(int x, int y) { while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) y = fath[top[y]]; else x = fath[top[x]]; } if (dep[x] < dep[y]) swap(x, y); return y; } int n, m, s; int main() { scanf("%d%d%d", &n, &m, &s); int u, v; for (int i = 1; i < n; i++) { scanf("%d%d", &u, &v); add_edge(u, v), add_edge(v, u); } dfs(s, 0), dfs2(s, s); for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); printf("%d\n", lca(x, y)); } return 0; }