-版本分支-

#版本分支
链接: link.
题目
小明负责维护公司一个奇怪的项目。这个项目的代码一直在不断分支(branch)但是从未发生过合并(merge)。
现在这个项目的代码一共有N个版本,编号1~N,其中1号版本是最初的版本。
除了1号版本之外,其他版本的代码都恰好有一个直接的父版本;即这N个版本形成了一棵以1为根的树形结构。
如下图就是一个可能的版本树:
1
/ \
2 3
| / \
5 4 6
现在小明需要经常检查版本x是不是版本y的祖先版本。你能帮助小明吗?
输入
第一行包含两个整数N和Q,代表版本总数和查询总数。
以下N-1行,每行包含2个整数u和v,代表版本u是版本v的直接父版本。
再之后Q行,每行包含2个整数x和y,代表询问版本x是不是版本y的祖先版本。
对于30%的数据,1 <= N <= 1000 1 <= Q <= 1000
对于100%的数据,1 <= N <= 100000 1 <= Q <= 100000
输出
对于每个询问,输出YES或NO代表x是否是y的祖先。
题解:
空间换时间
并不需要记x节点的每一个父节点,我们只需要预处理出他的2^i次方的父节点即可 例如想要查x往上7个节点,转化为二进制111即我们只需 100 + 10 + 1 转化为三次连跳即刻
二进制的每一位取与不取即可凑出任意一个数
所以当且仅当 u节点比v节点浅且 v跳到和u一个深度时恰好是u才对。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <cmath> 
#include <stack>
#define x first
#define y second
 
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef unsigned long long ULL;
const int N = 4e5 + 10, M = 1100, INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int h[N], e[N], ne[N], idx;
int dep[N], f[N][22]; // dep[x]: x节点的深度(从上往下看) f[x][i]: x的2^i祖先节点是什么 
int n, m;
void add(int a, int b) { //邻接表 
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int fa, int x) {  //预处理出节点深度以及2^i的父节点 
	f[x][0] = fa;
	dep[x] = dep[fa] + 1;
	for (int i = 1; (1 << i) <= dep[x]; i ++ ) 
		f[x][i] = f[f[x][i - 1]][i - 1];  //小技巧,x的2^i == x的 2 ^ i - 1 的 2 ^ i - 1 
	for (int i = h[x]; ~i; i = ne[i]) //往下搜 
		if (e[i] != fa)
			dfs(x, e[i]); 
}
int lca(int x, int y) {  //可以找最小公共祖先 
	if (dep[x] < dep[y]) swap(x, y);  //x要更深 
	for (int i = 20; i >= 0; i -- )
		if (dep[x] - (1 << i) >= dep[y]) //跳完以后还深就接着跳 
			x = f[x][i];
	if (x == y)	return x;  //一个深度时是一个点就说明时这个点 
	for (int i = 20; i >= 0; i -- )  //往上找最小祖宗节点 
		if (f[x][i] != f[y][i]) // 如果不相等就往上跳,那么所有不相等的都跳完了,在往上一个就是相等的了(不能从上往下,是一个就往上跳,因为这样又可能跳到的不是LCA) 
			x = f[x][i], y = f[y][i];
	return f[x][0]; // 最后一个不相等的再往上一级就是相等的了 
}  
int main() {
	memset(h, -1, sizeof h);
	cin >> n >> m;
	for (int i = 0; i < n - 1; i ++ ) {
		int a, b;
		cin >> a >> b;
		add(a, b), add(b, a);
	}
	dfs(0, 1);
	while (m --) {
		int u, v;
		cin >> u >> v;
		if (dep[u] > dep[v])	puts("NO");  
		else if (u == v)		puts("YES");
		else if (dep[u] == dep[v]) puts("NO");
		else {
			int len = dep[v] - dep[u]; // 预处理出高度差 
			for (int i = 0; i <= 20; i ++ )	 //转化为二进制后符合加法原理,可以即跳就行 
				if (len >> i & 1) v = f[v][i];
				if (v == u)	puts("YES");
				else		puts("NO");
		}
	}
  	return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值