-
数组模拟邻接表
https://blog.youkuaiyun.com/qq_17550379/article/details/94871801
该篇文章讲述数组模拟邻接表还算清楚,以下是我的补充理解:
e[x]中,x表示第x条边,存储的是这条边Lab中的端点b的编号。
head[k]中,k表示编号为k的顶点,存储的是由端点k为头顶点的单链表中,该头顶点指向的第一个顶点所在边的序号。
ne[x]存储的是,在单链表中,e[x]指向的下一个顶点所在边的序号。
-
LCA问题
LCA(Least Common Ancestors)最近公共祖先问题,是指在有根树中,找出某两个结点u和v最近的公共祖先。
1.Tarjan算法
Tarjan算法是求解有向图强连通分量的算法,可以用来离线求LCA,求解在进行tarjan算法的过程中完成,所以一般将多个查询打包成查询组进行一遍tarjan算法进行批量求解。
求解时首先深度遍历整棵树,在遍历时用并查集对节点进行合并,并检查是否有查询与当前已合并的节点相关,是则输出答案。
Tarjan算法中的三类点:
1.st[]=0 未搜索过的点
2.st[]=1 正在搜索的点
3.st[]=2 已经回溯过的点
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 10010, M = N * 2;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
int p[N];
int res[M * 2];
int st[N];
vector<PII> query[N]; // first存查询的另外一个点,second存查询编号
void add(int a, int b, int c)//加边
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u, int fa)//计算出每个顶点到根节点的距离
{
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
dist[j] = dist[u] + w[i];
dfs(j, u);
}
}
int find(int x)//合并集合同时路径压缩
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void tarjan(int u)//求LCA
{
st[u] = 1;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!st[j])
{
tarjan(j);
p[j] = u;
}
}
for (auto item : query[u])
{
int y = item.first, id = item.second;
if (st[y] == 2)
{
int anc = find(y);
res[id] = dist[u] + dist[y] - dist[anc] * 2;
}
}
st[u] = 2;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
if (a != b)
{
query[a].push_back({b, i});
query[b].push_back({a, i});
}
}
for (int i = 1; i <= n; i ++ ) p[i] = i;
dfs(1, -1);
tarjan(1);
for (int i = 0; i < m; i ++ ) printf("%d\n", res[i]);
return 0;
}
注:具体视频见acwing算法提高课