每天学一丢意为每天学一点丢一点。
LCA
L
C
A
LCA
LCA 上一篇中已经解释过,而
T
a
r
j
a
n
Tarjan
Tarjan 离线算法可以在
O
(
n
+
q
)
O(n+q)
O(n+q)的时间复杂度下,求得询问两点的
L
C
A
LCA
LCA。
而倍增是一种在线查询的算法,他的预处理复杂度是
O
(
n
log
n
)
O(n\log n)
O(nlogn),每次查询是
O
(
log
n
)
O(\log n)
O(logn),也就是复杂度是
O
(
n
log
n
)
O(n\log n)
O(nlogn) 的。
思路
两个同深度的节点,到 L C A LCA LCA 的距离一定是相同的,而我们将这个距离转化成一个二进数数,那么就只需要 log n \log n logn 次跳转就可以找到。而为了快速实现这个跳转,我们就需要记录每个节点往上跳转各个 2 i 2^i 2i 的节点位置。而这个可以通过一遍 d f s dfs dfs 维护。
代码
const int maxm = 20; //(1<<maxm)>maxn
vector<pair<int, int> >G[maxn];
int fa[maxn][maxm], deep[maxn];
int vis[maxn], dis[maxn];
void init(){
rep(i, 0, maxn)
G[i].clear();
mm(vis, 0);
}
void dfs(int x){
vis[x] = 1;
rep(i, 1, maxm)
fa[x][i] = fa[fa[x][i-1]][i-1];
rep(i, 0, G[x].size()){
if(!vis[G[x][i].fi]){
deep[G[x][i].fi] = deep[x]+1;
dis[G[x][i].fi] = dis[x]+G[x][i].se;
fa[G[x][i].fi][0] = x;
dfs(G[x][i].fi);
}
}
}
int query(int a, int b){
int x = a, y = b, lca, len;
if(deep[a] > deep[b]) swap(a, b);
int tmp = deep[b] - deep[a];
for(int i=0; (1<<i)<=tmp; i++)
if((1<<i)&tmp) b = fa[b][i];
per(i, 0, maxm)
if(fa[a][i] != fa[b][i])
a = fa[a][i], b = fa[b][i];
lca = (a==b)?a:fa[a][0];
len = dis[x]+dis[y]-2*dis[lca];
return len;
}
void addedge(int u, int v, int w){
G[u].push_back(mk(v, w));
G[v].push_back(mk(u, w));
}