祖孙询问
// 祖孙询问.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
/*
* https://loj.ac/p/10135
http://ybt.ssoier.cn:8088/problem_show.php?pid=1557
已知一棵 n 个节点的有根树。有 m 个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y 的祖孙关系。
【输入】
输入第一行包括一个整数 n 表示节点个数;
接下来 n 行每行一对整数对 a 和 b 表示 a 和 b 之间有连边。如果 b 是 −1,那么 a 就是树的根;
第 n+2 行是一个整数 m 表示询问个数;
接下来 m 行,每行两个正整数 x 和 y,表示一个询问。
【输出】
对于每一个询问,若 x 是 y 的祖先则输出 1,若 y 是 x 的祖先则输出 2,否则输出 0。
【输入样例】
10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19
【输出样例】
1
0
0
0
2
【提示】
数据范围与提示:
对于 30% 的数据,1≤n,m≤103 ;
对于 100% 的数据,1≤n,m≤4×104 ,每个节点的编号都不超过 4×104 。
*/
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 40010, M = N * 2;
int n, m; int root;
int h[N], e[M], ne[M], idx;
int depth[M], fa[N][35];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void bfs(int root) {
memset(depth, 0x3f, sizeof depth);
depth[0] = 0;
depth[root] = 1;
queue<int> q;
q.push(root);
while (q.size()) {
int t = q.front(); q.pop();
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (depth[j] > depth[t] + 1) {
depth[j] = depth[t] + 1;
q.push(j);
fa[j][0] = t;
for (int k = 1; k <= 15; k++) {
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
}
int lca(int a, int b) {
if (depth[a] < depth[b]) swap(a, b);
for (int k = 20; k >= 0; k--) {
if (depth[fa[a][k]] >= depth[b]) {
a = fa[a][k];
}
}
if (a == b) return a;
for (int k = 20; k >= 0; k--) {
if (fa[a][k] != fa[b][k]) {
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d",&n);
for (int i = 1; i <= n; i++) {
int a, b; scanf("%d%d",&a,&b);
if (b == -1) root = a;
else {
add(a, b); add(b, a);
}
}
bfs(root);
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
int a, b; cin >> a >> b;
int ret = lca(a, b);
if (ret == a) printf("1\n");
else if (ret == b) printf("2\n");
else printf("0\n");
}
return 0;
}
//来一套 tarjan离线lca
// 11111111.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
// 祖孙询问.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
/*
* https://loj.ac/p/10135
http://ybt.ssoier.cn:8088/problem_show.php?pid=1557
已知一棵 n 个节点的有根树。有 m 个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y 的祖孙关系。
【输入】
输入第一行包括一个整数 n 表示节点个数;
接下来 n 行每行一对整数对 a 和 b 表示 a 和 b 之间有连边。如果 b 是 −1,那么 a 就是树的根;
第 n+2 行是一个整数 m 表示询问个数;
接下来 m 行,每行两个正整数 x 和 y,表示一个询问。
【输出】
对于每一个询问,若 x 是 y 的祖先则输出 1,若 y 是 x 的祖先则输出 2,否则输出 0。
【输入样例】
10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19
【输出样例】
1
0
0
0
2
【提示】
数据范围与提示:
对于 30% 的数据,1≤n,m≤103 ;
对于 100% 的数据,1≤n,m≤4×10^4 ,每个节点的编号都不超过 4×10^4 。
*/
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 40010;
int h[N], e[2 * N], ne[2 * N], idx;
int depth[N], vis[N];
vector<vector<int>> queries[N];
int f[N]; int ans[N];
int n, m;
int root;
int find(int x) {
if (x != f[x]) f[x] = find(f[x]);
return f[x];
}
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int fa) {
for(int i = h[u];i!=-1;i=ne[i]) {
int v = e[i];
if (v == fa) continue; // 避免回到父节点
depth[v] = depth[u] + 1;
dfs(v, u);
}
}
void tarjan(int u) {
vis[u] = 1; // 标记当前节点已访问
for(int i= h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (!vis[v]) { // 如果子节点未被访问
tarjan(v); // 递归访问子节点
f[v] = u;
}
}
vis[u] = 2;
for (auto& e : queries[u]) {
int v = e[0]; int id = e[1]; int first = e[2];
if (vis[v] == 2 && ans[id] == -1) {
int anc = find(v);
if(anc==e[2]) {
ans[id] = 1; // u 是 v 的祖先
} else if (anc == u) {
ans[id] = 2; // v 是 u 的祖先
} else {
ans[id] = 0; // 无祖孙关系
}
}
}
}
int main()
{
memset(ans, -1, sizeof ans);
memset(h, -1, sizeof h);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int a, b; scanf("%d %d",&a,&b);
if (b == -1) { root = a;}
else {
add(a, b); add(b, a);
}
}
depth[root] = 1;
dfs(root, -1); //获取每个节点的深度
scanf("%d", &m);
for (int i = 0; i < m; i++) {
int a, b;
scanf("%d %d", &a, &b);
queries[a].push_back(vector<int>{b,i,a});
queries[b].push_back(vector<int>{a,i,a });
}
for (int i = 0; i < N; i++) { f[i] = i; }
tarjan(root);
for (int i = 0; i < m; i++) {
cout << ans[i] << endl;
}
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驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力

