祖孙询问

// 祖孙询问.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;
}

posted on 2025-04-21 17:47  itdef  阅读(10)  评论(0)    收藏  举报

导航