lca
作者:
Lemmon_kk
,
2020-07-17 09:47:11
,
所有人可见
,
阅读 526
向上标记法 O(mn)
不常用
倍增 mlog(n)
(1) 先把高层的向低层的跳
(2) 两个同时跳到lca的儿子
预处理 nlog(n)
查询 log(n)
设置哨兵: depth[0] = 0
int depth[N], fa[N][16];
int q[N];
void bfs(int root){
memset(depth, 0x3f, sizeof depth);
depth[0] = 0; depth[root] = 1;
int hh = 0, tt = 0;
q[0] = root;
while(hh <= tt){
int t = q[hh ++ ];
for(int i = h[t];~i;i = ne[i]){
int j = e[i];
if(depth[j] > depth[t] + 1){
depth[j] = depth[t] + 1;
q[ ++ tt] = j;
fa[j][0] = t;
for(int k = 1;k <= 15;k ++ ){
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
}
int lca(int a,int b){
if(depth[a] < depth[b]) swap(a, b);
// 1.
for(int k = 15;~k;k -- ){
if(depth[fa[a][k]] >= depth[b])
a = fa[a][k];
}
if(a == b) return a;
// 2.
for(int k = 15;~k;k -- ){
if(fa[a][k] != fa[b][k])
a = fa[a][k], b = fa[b][k];
}
return fa[a][0];
}
tarjan(离线算法) O(n + m)
点分三类 已经访问的, 且回溯过的(标记为 2)
未访问的(标记为 0)
正在访问的(标记为 1)
int st[N];
int res[N];
// fisrt 是另外一个点 second 是 query_id
vector<PII> query[N];
int find(int x){
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void tarjan(int u){
st[u] = 1;
for(int i = h[u];~i;i = ne[i]){
int j = e[i];
if(!st[j]){
tarjan(j);
p[find(j)] = find(u);
}
}
for(int i = 0;i < query[u].size();i ++ ){
int id = query[u][i].second;
int y = query[u][i].first;
if(st[y] == 2){
int lca = find(y);
ans[id] = lca;
}
}
st[u] = 2;
}
RMQ
树链剖分
%%%