DFN序求LCA
这是一种 \(O(n \log n)\) 预处理,\(O(1)\) 查询的神奇求 LCA 科技
实际效率上和树链剖分差不多,但主要胜在代码短一点,查询快一些
那具体是怎么个事呢?
考虑对于下面这张图
显然,lca 节点的 DFN 序 是(这个子树)最小的
然后往下遍历到 \(u\) ,回到 lca 然后向右遍历到 \(v\) ,中途必然遍历到 lca 的一个叶子节点 \(d\) 且 \(v\) 在 \(d\) 的子树上
如图
那么显然, \(d\) 的 dfn 序就在 \(u,v\) 之间,且 \(d\) 是这段区间中深度最浅的那一个
所以,我们之只用求出 \([dfn[u]+1,dfn[v]]\) 之间的深度最浅的节点即可,而 LCA 就是这两个节点的父亲
然后就可以使使用 ST 表实现 \(O(n \log n)\) 预处理, \(O(1)\) 查询了
代码如下
#include <bits/stdc++.h>
using namespace std;
int n, dfn, m, s;
struct node
{
int fa;
int dfn;
vector<int> son;
};
int st[20][1001000];
node tr[1000100];
int dfnmin(int x, int y)
{
return tr[x].dfn < tr[y].dfn ? x : y;
}
void dfs(int x)
{
tr[x].dfn = ++dfn;
st[0][tr[x].dfn] = tr[x].fa;
for (int son : tr[x].son)
{
if (son != tr[x].fa)
{
tr[son].fa = x;
dfs(son);
}
}
}
void init()
{
for (int i = 1; i <= __lg(n); i++)
{
for (int ww = 1; ww <= n; ww++)
{
st[i][ww] = dfnmin(st[i - 1][ww], st[i - 1][ww + (1 << (i - 1))]);
}
}
}
int get_lca(int a, int b)
{
if (a == b)
{
return a;
}
int u = tr[a].dfn, v = tr[b].dfn;
if (u > v)
{
swap(u, v), swap(a, b);
}
int d = __lg(v - u);
u++;
return dfnmin(st[d][u], st[d][v - (1 << d) + 1]);
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> s;
int a, b;
for (int ww = 1; ww < n; ww++)
{
cin >> a >> b;
tr[a].son.push_back(b);
tr[b].son.push_back(a);
}
dfs(s);
init();
for(int ww=1;ww<=m;ww++)
{
cin>>a>>b;
cout<<get_lca(a,b)<<"\n";
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】