abc 267 f 题解

abc 267 f

题意#

给定一个有 N 个结点的树,结点分别编号为 1,2,,N,第 i 条边 (1iN1) 连接点 Ai,Bi

树上的两个结点 u,v 之间的距离定义为从 uv 的最短路径所经过的边的数量。

给定 Q 次询问,第 i 次询问给出 Ui,Ki,请你输出任意一个与 Ui 距离为 Ki 的点的编号。如果不存在,输出 -1

思路#

在线#

首先,我们先思考这样一个问题,在什么情况下,不存在与点 u 距离为 k 的点?

当从 u 出发的最长路小于 k 时,就说明不存在这样的点。

而我们可以想到最长路这件事,就可以联想到树的直径。

令从 u 出发的最长路所到达的点为 v

可以证明,v 肯定是某一条树的直径的某一个端点,因为有一条直径的某个端点本身就是从点 u 开始的能走到的最远的点,所以 v 一定是这个搜到的点。我们设这条直径的两个端点为 s,t

所以,我们可以画一个这样的图:

也就是说,如果我们要求与 u 距离为 k 的点,就相当于在以 s 为根的树和以 t 为根的树上找 uk 级祖先。

代码#

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;

int n, q, d, id[2], fa[2][20][N];
vector<int> g[N];

void dfs(int t, int f, int dis, int p) {
  if (dis > d) {
    d = dis, id[p] = t;
  }
  if (p >= 2) {
    fa[p % 2][0][t] = f;
  }
  for (int i : g[t]) {
    if (i != f) {
      dfs(i, t, dis + 1, p);
    }
  }
}

void get_fa() {
  for (int i = 0; i < 2; i++) {
    for (int j = 1; j < 20; j++) {
      for (int k = 1; k <= n; k++) {
        fa[i][j][k] = fa[i][j - 1][fa[i][j - 1][k]];
      }
    }
  }
}

int Find(int u, int k, bool p) {
  for (int i = 0; i < 20; i++, k >>= 1) {
    if (k & 1) {
      u = fa[p][i][u];
    }
  }
  return u;
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  for (int i = 1, a, b; i < n; i++) {
    cin >> a >> b;
    g[a].push_back(b), g[b].push_back(a);
  }
  dfs(1, 0, 0, 0), d = 0, dfs(id[0], 0, 0, 1);
  dfs(id[0], 0, 0, 2), dfs(id[1], 0, 0, 3), get_fa();
  cin >> q;
  while (q--) {
    int u, k;
    cin >> u >> k;
    int s = max(Find(u, k, 0), Find(u, k, 1));
    cout << (!s ? -1 : s) << '\n';
  }
  return 0;
}

离线#

我们可以把每个点 u 需要查询的每个 k 都记录下来,然后对树做一遍深搜,用栈来维护每一条树链的元素,记录每次询问的答案。

代码#

#include <bits/stdc++.h>

using namespace std;
using pii = pair<int, int>;

const int N = 2e5 + 10;

int n, q, d, id[2], ans[N];
vector<int> g[N];
vector<pii> pos[N];
int top, stk[N];

void dfs(int t, int f, int dis, int p) {
  if (dis > d) {
    d = dis, id[p] = t;
  }
  for (int i : g[t]) {
    if (i != f) {
      dfs(i, t, dis + 1, p);
    }
  }
}

void _dfs(int t, int fa) {
  stk[++top] = t;
  for (int i : g[t]) {
    if (i != fa) {
      _dfs(i, t);
    }
  }
  for (auto [i, j] : pos[t]) {
    if (top > i) {
      ans[j] = stk[top - i];
    }
  }
  top--;
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  for (int i = 1, a, b; i < n; i++) {
    cin >> a >> b;
    g[a].push_back(b), g[b].push_back(a);
  }
  dfs(1, 0, 0, 0), d = 0, dfs(id[0], 0, 0, 1);
  cin >> q;
  for (int i = 1, u, k; i <= q; i++) {
    cin >> u >> k, ans[i] = -1;
    pos[u].push_back({k, i});
  }
  _dfs(id[0], 0), _dfs(id[1], 1);
  for (int i = 1; i <= q; i++) {
    cout << ans[i] << '\n';
  }
  return 0;
}

作者:cn

出处:https://www.cnblogs.com/chengning0909/p/17400258.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   chengning0909  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示