HDU - 6031 Innumerable Ancestors

本文介绍了一种解决树上查询问题的方法,利用LCA算法寻找两节点间的最低公共祖先,以最大化其深度。通过预处理提升效率,并采用排序和滑动窗口策略优化查询过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给一棵树,m次查询, 每次查询给两个集合, 从这两个集合里分别选一个结点,使得这两个节点的lca的深度最大。

考虑dfs序为5,6,7的三个节点,5,6的lca深度一定大于等于5,7的lca深度

所以可以讲查询集合按dfs序排序,然后类似尺取的操作,使得每一对dfs序接近的节点都做一次lca,选一个最深的就可以了

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
vector<int>w[maxn];
int dep[maxn], fa[maxn], st[maxn], up[maxn][21], n, tot;
void init(){
	for(int i = 1; i <= n; i++)
		up[i][0] = fa[i];
	for(int i = 1; i <= 20; i++)
		for(int j = 1; j <= n; j++)
			up[j][i] = up[up[j][i - 1]][i - 1];
	return ;
}
int lca(int u, int v){
	if(dep[u] < dep[v])
	swap(u, v);
	int dis = dep[u] - dep[v];
	for(int i = 0; i <= 20; i++){
		if(dis & (1 << i)){
			u = up[u][i];
		}
	}
	if(u == v)
	return u;
	for(int i = 20; i >= 0; i--){
		if(up[u][i] != up[v][i]){
			u = up[u][i];
			v = up[v][i];
		}
	}
	return fa[u];
}
void dfs(int f, int x){
	st[x] = tot++;
	fa[x] = f;
	dep[x] = dep[f] + 1;
	for(int i = 0; i < w[x].size(); i++){
		if(w[x][i] != f){
			dfs(x, w[x][i]);
		}
	}
	return ;
} 
int s1[maxn], s2[maxn];
int cmp(int u, int v){
	return st[u] < st[v];
}
int main(){
	int m, u, v, k1, k2;
	while(cin >> n >> m){
		for(int i = 1; i <= n; i++)
		w[i].clear();
		for(int i = 1; i < n; i++){
			scanf("%d %d", &u, &v);
			w[u].push_back(v);
			w[v].push_back(u);
		}
		tot = 0;
		dfs(0, 1);
		init();
		while(m--){
			int ans = 0;
			scanf("%d", &k1);
			for(int i = 0; i < k1; i++)
				scanf("%d", &s1[i]);
			scanf("%d", &k2);
			for(int i = 0; i < k2; i++)
				scanf("%d", &s2[i]);
			sort(s1, s1 + k1, cmp);
			sort(s2, s2 + k2, cmp);
			int i1 = 0, i2 = 0;
			while(i1 < k1 && i2 < k2){
				ans = max(ans, dep[lca(s1[i1], s2[i2])]);
			//	cout << s1[i1] << " " << s2[i2] << endl;
			//	cout << lca(s1[i1], s2[i2]) << endl;
				if(st[s1[i1]] > st[s2[i2]])
				i2++;
				else
				i1++;
			}
			printf("%d\n", ans);
		}
	}
	return 0;
} 
还有一种做法,复杂度比这个高,二分深度,然后把第一个集合所有在此深度的父节点存到set或map里,在判断第二个集合所有节点有没有相同的此深度的父节点。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值