Loading

LCA 模板 --2023.2.14

 
如果题目给定一个节点的父亲是谁,那么只需要建单向边就可以。
此题没有给定,只需要转化一下,维护一个pre数组记录这个节点的上一个点是谁就可以(或者在dfs时记录一个from)。
倍增法求LCA大体的思路是这样的:首先从树根dfs一遍求出所有节点到树根的距离(并且在点之间模拟出一条有向边),接着倍增法预处理出每个节点往上跳2的幂次方到达的点。面对询问时首先将两个两个点调整到同一深度,令dist[z] = dist[a] - dist[b] ( dist[a] > dist[b] ),接着循环一次求lowbit(z),代表这一位跳。调整到同一深度后检查一下是否两个点已经相同,相同的话直接输出。否则就是一步奇妙操作 : 循环21位如果两个点向上跳这一位相同的话就不跳,否则就同时向上跳这一位无限逼近LCA,最终任意一个点往上跳一步就是LCA。
代码 :
#include <bits/stdc++.h>
#define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
//#define endl '\n';
const string YES = "Yes";
const string NO = "No";

const int N = 500010;
vector<int> edge[N];
int f[N][22];
int dist[N];
void dfs(int now, int father) {
    f[now][0] = father;
    dist[now] = dist[father] + 1;
    for (auto y : edge[now])
        if (y != father)
            dfs(y, now);
}
int main() {
    gogo;
    int n, q, s;
    cin >> n >> q >> s;
    for (int i = 1;i < n;i ++) {
        int x, y;
        cin >> x >> y;
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    dfs(s, 0);
    for (int i = 1;i <= 21;i ++)
        for (int j = 1;j <= n;j ++)
            if (f[j][i - 1]) {
                f[j][i] = f[f[j][i - 1]][i - 1];
            }
    while (q --) {
        int a, b;
        cin >> a >> b;
        if (dist[a] < dist[b])
            swap(a, b);
        int z = dist[a] - dist[b];
        for (int j = 0;j <= 21 && z;j ++, z /= 2) {
            if (z & 1) {
                a = f[a][j];
            }
        }
        if (a == b) {
            cout << a << '\n';
            continue;
        }
        for (int j = 21;j >= 0;j --)
            if (f[a][j] != f[b][j]) {   
                a = f[a][j], b = f[b][j];
            }
        cout << f[a][0] << '\n';
    }
    return 0;
}
dfs记录form
#include <bits/stdc++.h>
#define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
//#define endl '\n';
const string YES = "Yes";
const string NO = "No";

const int N = 500010;
vector<int> edge[N];
int f[N][22];
int dist[N], pre[N];
void dfs(int u) {
    for (auto y : edge[u]) {
        if (y != pre[u]) {
            pre[y] = u;
            dist[y] = dist[u] + 1;
            f[y][0] = u;
            dfs(y);
        }
    }
}
int main() {
    gogo;
    int n, q, s;
    cin >> n >> q >> s;
    for (int i = 1;i < n;i ++) {
        int x, y;
        cin >> x >> y;
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    pre[s] = 0;
    dfs(s);
    for (int i = 1;i <= 21;i ++)
        for (int j = 1;j <= n;j ++)
            if (f[j][i - 1]) {
                f[j][i] = f[f[j][i - 1]][i - 1];
            }
    while (q --) {
        int a, b;
        cin >> a >> b;
        if (dist[a] < dist[b])
            swap(a, b);
        int z = dist[a] - dist[b];
        for (int j = 0;j <= 21 && z;j ++, z >>= 1) {
            if (z & 1) {
                a = f[a][j];
            }
        }
        if (a == b) {
            cout << a << '\n';
            continue;
        }
        for (int j = 21;j >= 0;j --)
            if (f[a][j] != f[b][j]) {
                a = f[a][j], b = f[b][j];
            }
        cout << f[a][0] << '\n';
    }
}
维护pre数组

还可以预处理出lg[]数组优化一下常数,以后闲的时候把这个板子更新一下。

posted @ 2023-02-14 22:26  KakaDBL  阅读(23)  评论(0)    收藏  举报