距离 点的距离,LCA 和tarjan离线LCA
//tarjan 离线lca
// 距离.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
/*
https://www.acwing.com/problem/content/1173/
http://ybt.ssoier.cn:8088/problem_show.php?pid=1552
给定一棵 n 个点的树,Q 个询问,每次询问点 x 到点 y 两点之间的距离。
【输入】
第一行一个正整数 n,表示这棵树有 n 个节点;
接下来 n−1 行,每行两个整数 x,y表示 x,y 之间有一条连边;
然后一个整数 Q,表示有 Q 个询问;
接下来 Q 行每行两个整数 x,y 表示询问 x 到 y 的距离。
【输出】
输出 Q 行,每行表示每个询问的答案。
【输入样例】
6
1 2
1 3
2 4
2 5
3 6
2
2 6
5 6
【输出样例】
3
4
【提示】
数据范围与提示:
对于全部数据,1≤n≤105,1≤x,y≤n
*/
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 100005; // 最大节点数
const int M = 2 * N; // 最大边数(无向边要乘2)
// 链式前向星存图相关数组
int head[N]; // head[u]表示u的第一条边的编号
int e[M]; // to[i]表示第i条边的终点
int nxt[M]; // nxt[i]表示第i条边的下一条边的编号
int idx; // 边的总数(即当前可用的边的编号)
int n, Q; // n为节点数,Q为询问数
vector<pair<int, int>> query[N]; // 存每个点的所有询问,pair<另一个点, 询问编号>
int res[N]; // 存每个询问的答案
int dist[N]; // dist[i]表示i到根节点的距离
int p[N]; // 并查集数组
int st[N]; // 0:未访问, 1:正在访问, 2:已访问
// 并查集查找祖先
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 链式前向星加边函数(无向边要加两次)
void add(int a, int b) {
e[idx] = b; // 边的终点
nxt[idx] = head[a]; // 下一条边指向原来的第一条边
head[a] = idx++; // 更新第一条边为当前边
}
// 预处理每个点到根的距离
void dfs(int u, int fa) {
for (int i = head[u]; i != -1; i = nxt[i]) { // 遍历u的所有邻接点
int v = e[i];
if (v == fa) continue; // 跳过父节点,防止回头
dist[v] = dist[u] + 1; // 距离加1
dfs(v, u); // 递归处理子节点
}
}
// Tarjan离线LCA算法
void tarjan(int u) {
st[u] = 1; // 标记u正在访问
for (int i = head[u]; i != -1; i = nxt[i]) { // 遍历u的所有邻接点
int v = e[i];
if (!st[v]) { // 只访问未访问过的子节点
tarjan(v); // 递归访问
p[v] = u; // 回溯时将子节点的祖先设为当前节点
}
}
// 处理所有以u为端点的询问
for (auto& q : query[u]) {
int y = q.first, id = q.second;
if (st[y] == 2) { // 另一个点已访问完
int anc = find(y); // 查询LCA
res[id] = dist[u] + dist[y] - 2 * dist[anc]; // 距离公式
}
}
st[u] = 2; // 标记u访问完成
}
int main() {
scanf("%d", &n);
memset(head, -1, sizeof head); // 初始化head数组为-1
idx = 0; // 初始化边的编号
for (int i = 1; i < n; i++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b); // 加边(无向边要加两次)
add(b, a);
}
scanf("%d", &Q);
for (int i = 0; i < Q; i++) {
int a, b;
scanf("%d%d", &a, &b);
if (a != b) {
query[a].push_back({ b, i }); // 双向存储询问
query[b].push_back({ a, i });
}
}
for (int i = 1; i <= n; i++) p[i] = i; // 初始化并查集
memset(st, 0, sizeof st); // 初始化状态数组
dist[1] = 0; // 根节点距离为0
dfs(1, -1); // 预处理距离
tarjan(1); // 求LCA并回答所有询问
for (int i = 0; i < Q; i++) {
printf("%d\n", res[i]); // 输出答案
}
return 0;
}
// 1171. 距离.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
using namespace std;
/*
https://ac.nowcoder.com/acm/contest/968/A
https://loj.ac/d/588
https://www.acwing.com/problem/content/description/1173/
题目描述
给定一棵n个点的树,Q个询问,每次询问点x到点y两点之间的距离。
输入描述:
第一行一个正整数n,表示这棵树有n个节点;
接下来n-1行,每行两个整数x,y表示x,y之间有一条连边;
然后一个整数Q,表示有Q个询问;
接下来Q行每行两个整数x,y表示询问x到y的距离。
输出描述:
输出Q行,每行表示每个询问的答案。
6
1 2
1 3
2 4
2 5
3 6
2
2 6
5 6
输出样例:
3
4
链接:https://ac.nowcoder.com/acm/contest/968/A
对于全部数据,
1≤n≤10^5,1≤x,y≤n。
*/
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N = 100010; // 最大点数
const int LOG = 17; // 2^17 > 1e5,足够覆盖所有深度
int n, Q;
vector<int> g[N]; // 邻接表存树
int depth[N]; // depth[u]:u到根的深度
int fa[N][LOG]; // fa[u][k]:u的第2^k级祖先
// 预处理深度和倍增祖先
void dfs(int u, int father) {
fa[u][0] = father; // u的父亲
for (int k = 1; k < LOG; k++)
fa[u][k] = fa[fa[u][k - 1]][k - 1]; // u的2^k级祖先
for (int v : g[u]) {
if (v == father) continue; // 不走回头路
depth[v] = depth[u] + 1; // 子节点深度+1
dfs(v, u);
}
}
// 倍增法求LCA
int lca(int a, int b) {
if (depth[a] < depth[b]) swap(a, b); // 保证a不浅于b
// 先把a跳到和b同一深度
for (int k = LOG - 1; k >= 0; k--)
if (depth[fa[a][k]] >= depth[b])
a = fa[a][k];
if (a == b) return a; // b就是a祖先
// 一起往上跳,直到LCA
for (int k = LOG - 1; k >= 0; k--)
if (fa[a][k] != fa[b][k])
a = fa[a][k], b = fa[b][k];
return fa[a][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
// 读入树的边
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
// 以1为根节点,预处理深度和祖先
depth[1] = 0;
dfs(1, 0);
cin >> Q;
while (Q--) {
int x, y;
cin >> x >> y;
int anc = lca(x, y); // 最近公共祖先
// 距离 = 深度之和 - 2*LCA深度
cout << depth[x] + depth[y] - 2 * depth[anc] << '\n';
}
return 0;
}
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
浙公网安备 33010602011771号