DFS时间戳

时间戳

这就是树上查询问题 , 是求两个点有什么关系

让我们来看一下样例解释:
注意字母旁边的数字就是时间戳, a在先序遍历(遍历顺序 : 左,右,根)是第一个进, 第十六个出。

在时间戳里只要一个数比另一个数先进, 后出, 就是另一个数的祖先

题解:

  1. 对整棵树进行DFS。对每个点u记住dfs函数进入u的时间TI(u)以及离开u的时间TO(u),如上图所示,每个节点左边是其dfs进入时间,右边是离开的时间。
  2. 可以常数时间确定u是不是v的祖先,如果u的进入时间早于v且u的离开时间晚于v,则u是v的祖先:TI(u)<TI(v)<TO(v)<TO(u),建议读者验证上图中每一对u和v的时间戳。
  3. 预处理O(n),每个查询的处理时间O(1)。

代码:

// LOJ10135 祖孙询问
#include <bits/stdc++.h>
const int NN = 4e4 + 4;  // 定义常量 NN,表示最大可能的节点数加上一个小的常数
using namespace std;
vector<int> G[NN];  // G数组存储每个节点的子节点,用邻接表的形式表示图
// N为总节点数,TI和TO分别记录每个节点的进入和离开时间戳
int N, TI[NN], TO[NN], timer = 0;
// 深度优先搜索函数,用于遍历树并记录时间戳,
// u为当前节点,fa为父节点,-1表示无父节点
void dfs(int u, int fa = -1) {
  TI[u] = ++timer;           // 记录其进入时间戳
  for (int v : G[u])         // 遍历当前节点u的所有子节点v
    if (v != fa) dfs(v, u);  // 如果v不是u的父节点,则递归调用dfs
  TO[u] = ++timer;  // 遍历u的所有子树后,记录其离开时间戳
}
// 如果u的进入时间早于v且u的离开时间晚于v,则u是v的祖先
bool isAncestor(int u, int v) { return TI[u] < TI[v] && TO[v] < TO[u]; }
int solve(int u, int v) {
  if (isAncestor(u, v)) return 1;  // u是v的祖先
  if (isAncestor(v, u)) return 2;  // v是u的祖先
  return 0;                        // 否则,返回0
}
int main() {
  ios::sync_with_stdio(false), cin.tie(0);
  int N, M, root;  // 节点数,查询数,root为根节点
  cin >> N;        // 读入节点数
  for (int i = 1, u, v; i <= N; i++) {
    cin >> u >> v;
    if (v == -1)
      root = u;  // 如果v为-1,则u为根节点
    else
      G[u].push_back(v), G[v].push_back(u);  // 将u和v互相添加为邻接节点
  }
  dfs(root);  // 从根节点开始深度优先搜索,记录时间戳
  cin >> M;   // 读入查询数
  for (int i = 1, u, v; i <= M; i++) cin >> u >> v, printf("%d\n", solve(u, v));
  return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值