LCA(树上最近公共祖先)

LCA即是树上最近公共祖先算法,这里给出模板,主要以练习题集为主。

1、模板题

传送门

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int head[N], cnt;
struct node
{
    int to, nex;
} e[N << 1];
void add_edge(int u, int v)
{
    e[++cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt;
}
int dep[N], f[N][30], clg[N];
void dfs(int u, int fa)
{
    f[u][0] = fa;
    dep[u] = dep[fa] + 1;
    for(int i = 1; i <= clg[dep[u]]; ++i)
    {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for(int i = head[u]; i; i = e[i].nex)
    {
        if(e[i].to != fa) dfs(e[i].to, u);
    }
}
int lca(int x, int y)
{
    if(dep[x] < dep[y]) swap(x, y);
    while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];
    if(x == y) return x;
    for(int i = clg[dep[x]] - 1; i >= 0; --i)
    {
        if(f[x][i] != f[y][i])
        {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
int n, m, s;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> m >> s;
    int x, y;
    for(int i = 1; i <= n - 1; ++i)
    {
        cin >> x >> y;
        add_edge(x, y);
        add_edge(y, x);
    }
    for(int i = 1; i <= n; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);
    dfs(s, 0);
    while(m--)
    {
        cin >> x >> y;
        cout << lca(x, y) << "\n";
    }
    return 0;
}

2、树上任意两点距离

传送门

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 4e4 + 100;
int cnt, head[N];
struct node
{
    int to, nex, w;
} e[N << 1];
void add_edge(int u, int v, int w)
{
    e[++cnt].to = v;
    e[cnt].w = w;
    e[cnt].nex = head[u];
    head[u] = cnt;
}
int dep[N], clg[N], f[N][30], dis[N];
void dfs(int u, int fa)
{
    f[u][0] = fa;
    dep[u] = dep[fa] + 1;
    for(int i = 1; i <= clg[dep[u]]; ++i)
    {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v != fa)
        {
            dis[v] = dis[u] + e[i].w;
            dfs(v, u);
        }
    }
}
int lca(int x, int y)
{
    if(dep[x] < dep[y]) swap(x, y);
    while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];
    if(x == y) return x;
    for(int i = clg[dep[x]] -  1; i >= 0; --i)
    {
        if(f[x][i] != f[y][i])
        {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
//公式
int cal_dis(int u, int v)
{
    return dis[u] + dis[v] - 2 * dis[lca(u, v)];
}
int n, q, u, v, w;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    for(int i = 1; i < N; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);
    int t;
    cin >> t;
    while(t--)
    {
        cnt = 0;
        memset(head, -1, sizeof head);
        dis[1] = dep[1] = 0;
        cin >> n >> q;
        for(int i = 1; i <= n - 1; ++i)
        {
            cin >> u >> v >> w;
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        dfs(1, 0);
        while(q-- && cin >> u >> v) cout << cal_dis(u, v) << "\n";
    }
    return 0;
}

3、小A的最短路

Solution
传送门

因为有两个点之间通过免费,可以使用s-t作为中转,计算所有情况最小即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int cnt, head[N];
struct node
{
    int to, nex;
} e[N << 1];
void add_edge(int u, int v)
{
    e[++cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt;
}
int dep[N], clg[N], f[N][30];
void dfs(int u, int fa)
{
    f[u][0] = fa;
    dep[u] = dep[fa] + 1;
    for(int i = 1; i <= clg[dep[u]]; ++i)
    {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for(int i = head[u]; i; i = e[i].nex)
    {
        if(e[i].to != fa) dfs(e[i].to, u);
    }
}
int lca(int x, int y)
{
    if(dep[x] < dep[y]) swap(x, y);
    while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];
    if(x == y) return x;
    for(int i = clg[dep[x]] - 1; i >= 0; --i)
    {
        if(f[x][i] != f[y][i])
        {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
int cal_dis(int x, int y)
{
    //cout << x << " " << y << " " << lca(x, y) << "lca\n";
    return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
int n, m, s, t;

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    cnt = 0;
    memset(head, -1, sizeof head);
    cin >> n;
    for(int i = 1; i <= n; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);
    int x, y;
    for(int i = 1; i <= n - 1; ++i)
    {
        cin >> x >> y;
        add_edge(x, y);
        add_edge(y, x);
    }
    dfs(1, 0);
    cin >> s >> t;
    cin >> m;
    while(m-- && cin >> x >> y)
    {
        int ans = cal_dis(x, y);
        ans = min({ans, cal_dis(x, s) + cal_dis(t, y), cal_dis(x, t) + cal_dis(s, y)});
        cout << ans << "\n";
    }
    return 0;
}
posted @ 2022-06-27 09:59  std&ice  阅读(156)  评论(0)    收藏  举报