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; }
#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'; } }
还可以预处理出lg[]数组优化一下常数,以后闲的时候把这个板子更新一下。

浙公网安备 33010602011771号