【模板】最近公共祖先(LCA)

题意简述

给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

题解思路

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));
	}
}
原文地址:https://www.cnblogs.com/xuyixuan/p/9696617.html