题意简述
给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
题解思路
1.倍增,先将两个点跳至同一高度,再同时往上跳 ( 2 ^ n )高度
2.tarjan,离线,先将所有询问存起来,dfs一遍
3.树链剖分
代码(倍增)
#include <cstdio>
#include <algorithm>
using namespace std;
int n, m, s, u, v, cnt;
int h[501000], to[1001000], nxt[1001000], dep[501000], lg[501000];
int f[501000][25];
inline void add_edge(const int& u, const int& v)
{
to[++cnt] = v;
nxt[cnt] = h[u];
h[u] = cnt;
}
void dfs(const int& x)
{
for (register int i = 0; f[x][i]; ++i) f[x][i + 1] = f[f[x][i]][i];
for (register int i = h[x]; i; i = nxt[i])
if (to[i] ^ f[x][0])
{
dep[to[i]] = dep[x] + 1;
f[to[i]][0] = x;
dfs(to[i]);
}
}
inline int lca(int x, int y)
{
if (dep[x] < dep[y]) swap(x, y);
while (dep[x] > dep[y]) x = f[x][lg[dep[x] - dep[y]]];
if (x == y) return x;
for (register int i = lg[dep[x]] + 1; i--;)
if (f[x][i] ^ f[y][i])
x = f[x][i], y = f[y][i];
return f[x][0];
}
int main()
{
scanf("%d%d%d", &n, &m, &s);
for (register int i = 2; i <= n; ++i)
{
lg[i] = lg[i >> 1] + 1;
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
dfs(s);
for (register int i = 1; i <= m; ++i)
{
scanf("%d%d", &u, &v);
printf("%d
", lca(u, v));
}
}
代码(tarjan)
#include <cstdio>
int n, m, s, u, v, cnt, N;
int h[501000], to[2001000], nxt[2001000], ans[501000], fa[501000];
inline void add_edge(const int& u, const int& v)
{
to[++cnt] = v;
nxt[cnt] = h[u];
h[u] = cnt;
}
int find(const int& x) {return x ^ fa[x] ? fa[x] = find(fa[x]) : x;}
void dfs(int x)
{
fa[x] = x;
for (register int i = h[x]; i; i = nxt[i])
if (i < N && !fa[to[i]]) dfs(to[i]), fa[to[i]] = x;
else if (i >= N && fa[to[i]]) ans[(i - N) >> 1] = find(to[i]);
}
int main()
{
scanf("%d%d%d", &n, &m, &s);
N = 2 * n - 1;
n += m;
for (register int i = 1; i < n; ++i)
{
scanf("%d%d", &u, &v);
add_edge(u, v); add_edge(v, u);
}
dfs(s);
for (register int i = 0; i < m; ++i) printf("%d
", ans[i]);
}
代码(树链剖分)
#include <cstdio>
#include <algorithm>
int n, m, s, u, v, cnt;
int h[600000], nxt[1100000], to[1100000];
int f[600000], sz[600000], dep[600000], hvs[600000], top[600000];
inline void add_edge(const int& u, const int& v)
{
nxt[++cnt] = h[u];
to[cnt] = v;
h[u] = cnt;
}
void dfs1(const int& u)
{
sz[u] = 1;
for (register int i = h[u]; i; i = nxt[i])
if (to[i] ^ f[u])
{
dep[to[i]] = dep[u] + 1;
f[to[i]] = u;
dfs1(to[i]);
sz[u] += sz[to[i]];
if (!hvs[u] || sz[to[i]] > sz[hvs[u]]) hvs[u] = to[i];
}
}
void dfs2(const int& u, const int& tp)
{
top[u] = tp;
if (hvs[u]) dfs2(hvs[u], tp);
for (register int i = h[u]; i; i = nxt[i])
if (to[i] ^ f[u] && to[i] ^ hvs[u])
dfs2(to[i], to[i]);
}
inline int query(int u, int v)
{
for (; top[u] ^ top[v]; u = f[top[u]])
if (dep[top[u]] < dep[top[v]])
std::swap(u, v);
return dep[u] < dep[v] ? u : v;
}
int main()
{
scanf("%d%d%d", &n, &m, &s);
for (register int i = 1; i < n; ++i)
{
scanf("%d%d", &u, &v);
add_edge(u, v); add_edge(v, u);
}
f[s] = s;
dfs1(s); dfs2(s, s);
for (register int i = 1; i <= m; ++i)
{
scanf("%d%d", &u, &v);
printf("%d
", query(u, v));
}
}