給出乙個n節點,s為根的樹,m次詢問某兩個節點間的lca
n<=500000,m<=500000
lca的方法一般有倍增,tarjan,樹鏈剖分,這裡主要介紹樹鏈剖分
這個題用vector的樹鏈剖分會超時(常數原因)
vector的其他方法,開o2貌似能過
size[u]:以u為根節點的子樹的節點數
dep[u]:u結點的深度,樹根為0,往下依層遞增
樹鏈剖分是對樹上的邊按規則進行一些劃分:
重邊:節點與其重兒子(所有兒子中size最大的)所連邊
輕邊:節點與其非重兒子連邊
重鏈:重邊相連
輕鏈:輕邊
經過規則,發現重鏈的dep一定是單調的,即不會有這樣的重鏈:(粗邊是重鏈)
應當是這樣的:
引入乙個top[u],如果u在重鏈上,則top[u]能跳到這條重鏈上dep最小的那個點,對於輕鏈,會跳到自己頭上。
這些構建完成後,樹鏈剖分滿足定理:從根到任意乙個點,中間的重鏈數和輕鏈數均不會超過log n
在求lca時,如果乙個個節點跑,那麼面對m次查詢就會很差,甚至一次就需要n次計算。
但是樹鏈剖分中,可以在重鏈上跳,就大大縮短了計算的次數,意思就是如果當前點處於乙個重鏈上,可以直接跳到重鏈的top,直到兩者的top相同(即處在同一條重鏈上/處於同乙個輕邊的同乙個端點)
用fa陣列進行迭代,fa[top[u]]即可不論輕重邊一直跳。
中間過程都讓深度更大的乙個優先往上跳(淺的優先跳會跳的過多導致非「最近」公共祖先)
終止條件即兩個點跳到了同一條重鏈上/乙個輕點上,dep的單調性導致了兩個不同重鏈之間至少有乙個輕鏈,也就不會跳得太多
具體的過程需要兩個dfs,第一次先標記出son,第二次連線起來這些son
結構體存邊版
#include
"cstdlib"
#include
"cstdio"
#include
"iostream"
#include
"vector"
using
namespace std;
#define maxn 500005
int dep[maxn]
, fa[maxn]
, son[ maxn ]
, size [ maxn ]
, top [ maxn ]
;#define cur v[i].t
struct edge
v[maxn<<1]
;int last[maxn]
;void
dfs1
(int x )
}void
dfs2
(int x ,
int t)
inline
intread()
int tot =0;
inline
void
insert
(int
&from ,
int&to)
intmain()
dfs1
(s);
dfs2
(s ,s)
;for
(register
int i =
0; i < m ; i++
)printf
("%d\n"
,dep[f]
?f:g);}
return0;
}
類封裝+vector存邊(不開o2 t3個點,開o2,t2個點)
#include
#include
#include
using
namespace std;
class
tree_chain}}
void
dfs2
(int x,
int t)
//當前節點x,當前重鏈頂的編號
}void
insert
(int
&u,int
&v)int
lca(
int&u,
int&v)
return dep[u]
< dep[v]
? u : v;
}}tc;
intmain()
tc.dfs1
(s);
tc.dfs2
(s, s)
;for
(register
int i =
0; i < m; i++
)return0;
}
洛谷 P3379 LCA 模板題
傳送門 什麼是lca?具體請檢視下面鏈結 這是一道模板題。卡vector,卡cin.所以老實點用鄰接表和scanf把 上面說的很詳細啦 用的是倍增。暴力的優化。鬼知道我理解倍增理解了一下午emmm。主要是儲存和優化位置 然後!自信滿滿以為理解了。開始寫部落格了。發現。似乎?又什麼都不會了。我的內心 ...
樹鏈剖分求LCA的模板(洛谷P3379)
2020.5.6 題目描述我就不寫了,模板題。之前寫過倍增 tarjan求lca的板子,但是倍增這東西我貌似還沒太搞懂,只寫過st表,可能是我太菜了嗚嗚嗚。樹剖其實我之前也沒太弄懂,後來做了一道河南某年的省選題就豁然開朗了 但還沒ac,可能是我用的線段樹板子出問題了 就是用2遍dfs處理好樹內邊的歸...
Lca樹鏈剖分法
樹鏈剖分各陣列的作用 son 最重的兒子節點,即節點最多的那個 bulk 所有兒子節點的總和 包括兒子的兒子 dep 該節點的深度 ft 該節點的父親 top 鏈的最上方的節點 首先dfs預處理出前面的四個陣列。然後第二個dfs進行剖分,預處理出最後乙個陣列。例子 此時son 1 3 因為bulk ...