LCA:一棵树上两个点向上最近的一个点,我们可以先对其中一个点向上标记至根节点,再对另外一个点向上标记,第一次遇到第一次标记的点就是所求。
此外,我们可以采用倍增的思想记录每个点向上2^k步的父节点,首先预处理出每个点的深度,以及每个点向上2^k步的父节点。
当我们查询两个点的LCA时,先把他们调整到同一深度,再看他们是否相当,不等就同时向上调整。
模板题:LCA
#include
using namespace std;
const int maxn = 50010;
int f[maxn][20], d[maxn], dist[maxn];
int ver[2 * maxn], Next[maxn * 2], edge[maxn * 2], head[maxn];
int T, n, m, tot, t;
queueq;
void add(int x, int y, int z) {
ver[++tot] = y; edge[tot] = z; Next[tot] = head[x]; head[x] = tot;
}
void bfs() {
q.push(1); d[1] = 1;
while (!q.empty()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if (d[y])continue;
d[y] = d[x] + 1;
dist[y] = dist[x] + edge[i];
f[y][0] = x;
for (int j = 1; j <= t; j++)
f[y][j] = f[f[y][j - 1]][j - 1];
q.push(y);
}
}
}
int lca(int x, int y) {
if (d[x] < d[y])swap(x,y);
for (int i = t; i >= 0; i--) {
if (d[f[x][i]] >= d[y])x = f[x][i];
}
if (x == y)return x;
for (int i = t; i >= 0; i--) {
if (f[x][i] != f[y][i])x = f[x][i], y = f[y][i];
}
return f[x][0];
}
int main() {
cin >> T;
while (T--) {
cin >> n >> m;
t = (int)(log(n) / log(2)) + 1;
memset(head,0,sizeof(0));
memset(d,0,sizeof(0));
tot = 0;
for (int i = 1; i < n; i++) {
int x, y, z;
scanf("%d%d%dd",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
bfs();
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d",&x,&y);
printf("%d\n",dist[x]+dist[y]-2*dist[lca(x,y)]);
}
}
return 0;
}
Tarjan算法:
这是一个离线的求LCA的算法,先把所有要求的点存起来,然后在树上跑一遍dfs,并且标记哪些点是没有访问过的,哪些是已经访问过的,哪些已经访问完毕且回溯了;
#include
using namespace std;
#define ll long long
const int maxn = 50010;
int ver[maxn * 2], Next[maxn * 2], edge[maxn * 2], head[maxn];
int fa[maxn], d[maxn], v[maxn], lca[maxn], ans[maxn];
vector query[maxn], query_id[maxn];
int T, n, m, tot, t;
void add(int x, int y, int z) {
ver[++tot], edge[tot] = z, Next[tot] = head[x], ver[tot] = y; head[x] = tot;
}
void add_query(int x, int y, int id) {
query[x].push_back(y),query_id[x].push_back(id);
query[y].push_back(x),query_id[y].push_back(id);
}
int get(int x) {
return fa[x] == x ? x : fa[x] = get(fa[x]);
}
void tarjan(int x) {
v[x] = 1;
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if (v[y])continue;
d[y] = d[x] + edge[i];
tarjan(y);
fa[y] = x;
}
for (int i = 0; i < query[x].size(); i++) {
int y = query[x][i], id = query_id[x][i];
if (v[y] == 2) {
int lca = get(y);
ans[id] = min(ans[id],d[x]+d[y]-2*d[lca]);
}
}
v[x] = 2;
}
int main() {
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
head[i] = 0, fa[i] = i, v[i] = 0;
query[i].clear();
query_id[i].clear();
}
tot = 0;
for (int i = 1; i < n; i++) {
int x, y, z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d",&x,&y);
if (x == y)ans[i] = 0;
else {
add_query(x,y,i);
ans[i] = 1 << 30;
}
}
tarjan(1);
for (int i = 1; i <= m; i++)
printf("%d\n",ans[i]);
}
return 0;
}