33OJ
登录 注册

朴素

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500000;
int n, m, s; // 点数、询问数、起点
vector<int> e[MAXN + 5];
namespace work1
{
int fa[MAXN + 5];
int dep[MAXN + 5];
void dfs(int u, int father)
{
    fa[u] = father;
    dep[u] = dep[father] + 1;
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (v == father)
            continue;
        dfs(v, u);
    }
}
bool vis[MAXN + 5];
int lca(int u, int v)
{
    if (dep[u] > dep[v])
        swap(u, v);
    while (dep[u] != dep[v])
        v = fa[v];
    while (u != v)
        u = fa[u], v = fa[v];
    return u;
}
void main()
{
    dfs(s, 0);
    while (m--)
    {
        int u, v;
        cin >> u >> v;
        cout << lca(u, v) << "\n";
    }
}
} // namespace work1
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> s;
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    work1::main();
    return 0;
}

倍增

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500000;
const int LOGN = 20;
int n, m, s; // 点数、询问数、起点
vector<int> e[MAXN + 5];
int f[MAXN + 5][LOGN + 5];
int dep[MAXN + 5];
void dfs(int u, int fa)
{
    f[u][0] = fa;
    dep[u] = dep[fa] + 1;
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (v == fa)
            continue;
        dfs(v, u);
    }
}
bool vis[MAXN + 5];
int lca(int u, int v)
{
    if (dep[u] > dep[v])
        swap(u, v);
    for (int i = LOGN; i >= 0; i--)
        if ((1 << i) <= dep[v] - dep[u])
            v = f[v][i];
    if (u == v)
        return u;
    for (int i = LOGN; i >= 0; i--)
    {
        if (f[u][i] != f[v][i])
            u = f[u][i], v = f[v][i];
    }
    return f[u][0];
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> s;
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(s, 0);
    for (int i = 1; i <= LOGN; i++)
        for (int j = 1; j <= n; j++)
            f[j][i] = f[f[j][i - 1]][i - 1];

    while (m--)
    {
        int u, v;
        cin >> u >> v;
        cout << lca(u, v) << "\n";
    }
    return 0;
}

欧拉序+st表

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500'000;
int n, m, s;
vector<int> e[MAXN + 5];
// dis[i] 存 i 的深度
int dis[MAXN + 5];
// <深度, 点的编号>
vector<pair<int, int>> a;
// pos[i] 存 i 在 a 中第一次出现的下标
int pos[MAXN + 5];
void dfs(int u, int fa)
{
    a.push_back({dis[u], u});
    pos[u] = (int)a.size() - 1;
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (v == fa)
            continue;
        dis[v] = dis[u] + 1;
        dfs(v, u);
        a.push_back({dis[u], u});
    }
}
// 处理 a 数组的 st 表
// st[i][j] 存 a[i] 开始的 2^j 个元素中的最小值下标
int st[MAXN * 2 + 5][20];
void initST()
{
    int len = a.size();
    for (int i = 0; i < len; i++)
        st[i][0] = i;
    for (int j = 1; (1LL << j) <= len; j++)
    {
        for (int i = 0; i + (1LL << j) - 1 < len; i++)
        {
            // i 为起点,2^j 长度的最小值下标
            int L = st[i][j - 1];
            int R = st[i + (1LL << (j - 1))][j - 1];
            if (a[L].first < a[R].first)
                st[i][j] = L;
            else
                st[i][j] = R;
        }
    }
}
int lca(int u, int v)
{
    if (pos[u] > pos[v])
        swap(u, v);
    // 找到 pos[u]~pos[v] 之间 first 最小的下标
    int len = pos[v] - pos[u] + 1;
    int j = log2(len);
    // pos[u] 开头的 2^j 和 pos[v] 结尾的 2^j 的最小值的最小值
    int L = st[pos[u]][j];
    int R = st[pos[v] - (1LL << j) + 1][j];
    if (a[L].first < a[R].first)
        return a[L].second;
    else
        return a[R].second;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> s;
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dis[s] = 0;
    dfs(s, 0);
    initST();
    while (m--)
    {
        int u, v;
        cin >> u >> v;
        cout << lca(u, v) << "\n";
    }
    return 0;
}

Tarjan

dfs+并查集

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500000;
const int MAXM = 500000;
int n, m, s;
vector<int> e[MAXN + 5];
//(v, 问题 id)
vector<pair<int, int>> ask[MAXN + 5];
int ans[MAXM + 5]; // 问题 id 为 i 的问题答案
// 每个点是否搜过了
bool vis[MAXN + 5];
// 并查集
int fa[MAXN + 5];
int findRoot(int x)
{
    if (fa[x] == x)
        return x;
    return fa[x] = findRoot(fa[x]);
}
void dfs(int u, int from)
{
    for (int i = 0; i < ask[u].size(); i++)
        if (vis[ask[u][i].first])
            ans[ask[u][i].second] = findRoot(ask[u][i].first);
    vis[u] = true;
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (v == from)
            continue;
        dfs(v, u);
        fa[v] = u;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> s;
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    // 读询问
    for (int i = 1; i <= m; i++)
        ans[i] = 0;
    for (int i = 1; i <= m; i++)
    {
        int u, v, id;
        cin >> u >> v;
        id = i;
        if (u == v)
            ans[i] = u;
        else
        {
            ask[u].push_back({v, id});
            ask[v].push_back({u, id});
        }
    }
    // tarjan
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
        vis[i] = false;
    }
    dfs(s, 0);
    for (int i = 1; i <= m; i++)
        cout << ans[i] << "\n";
    return 0;
}

重链剖分

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500000;
int n, m, s;
vector<int> e[MAXN + 5];
int fa[MAXN + 5], dep[MAXN + 5], siz[MAXN + 5], hson[MAXN + 5];
void dfs(int u, int father)
{
    fa[u] = father;
    siz[u] = 1;
    hson[u] = 0;
    dep[u] = dep[father] + 1;
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (v == father)
            continue;
        dfs(v, u);
        siz[u] += siz[v];
        if (siz[v] > siz[hson[u]])
            hson[u] = v;
    }
}
// 每个点的 dfs 序、按dfs序第 i 名的点编号是 rnk[i],所属链顶端是 top
int tot, dfn[MAXN + 5], rnk[MAXN + 5], top[MAXN + 5];
void dfs_div(int u, int father)
{
    dfn[u] = ++tot;
    rnk[tot] = u;
    if (hson[u] != 0)
    {
        top[hson[u]] = top[u];
        dfs_div(hson[u], u);
    }
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (v == father || v == hson[u])
            continue;
        top[v] = v;
        dfs_div(v, u);
    }
}

int lca(int u, int v)
{
    while (top[u] != top[v])
    {
        if (dep[top[u]] < dep[top[v]])
            v = fa[top[v]];
        else
            u = fa[top[u]];
    }
    if (dep[u] > dep[v])
        return v;
    else
        return u;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> s;
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(s, 0);
    tot = 0;
    top[s] = s;
    dfs_div(s, 0);
    while (m--)
    {
        int u, v;
        cin >> u >> v;
        cout << lca(u, v) << "\n";
    }
    return 0;
}
分类: 模板 · 更新时间 2026-11-8 14:43:14