问题描述

一棵树,\(q\) 次询问给定 \(u,v\),保证 \(u,v\) 不为祖孙关系。设 \(p=\operatorname{lca}(u,v)\),求 \(p\) 的一个孩子 \(u'\) 且 \(u'\) 为 \(u\) 的祖先或 \(u\) 本身,类似需要求 \(v'\)。

朴素解法

倍增可以做到 \(\mathcal{O}(\log n)\),但太慢了。

优化解法

不妨令 \(\mathrm{idx}(u)<\mathrm{idx}(v)\),考虑到 DFS 序求 \(p=\operatorname{lca}(u,v)\) 最后一步跳父亲前,如果 ST 表对于相同深度的点,选取 \(\operatorname{idx}\) 大的,就是求出了 \(v'\),得到了 \(p\) 后,通过求 \(\operatorname{lca}(u,p)\) 最后一步不要跳父亲,就能得到 \(u'\)。

时间复杂度 \(\mathcal{O}(1)\)。

代码实现

#include <cstdio>
#include <iostream>
#include <vector>
#include <tuple>
#include <cassert>
using namespace std; const int N = 5e5 + 10;
const int lgN = __lg(N) + 1; int n, q, rt;
vector<int> e[N]; int st[lgN][N], idx[N], dpt[N], fa[N], timer, R[N]; void dfs(int u) {
st[0][idx[u] = ++timer] = u;
for (int v : e[u]) {
if (v == fa[u]) continue;
fa[v] = u;
dpt[v] = dpt[u] + 1;
dfs(v);
}
R[u] = timer;
}
inline int Min(int u, int v) {
return dpt[u] < dpt[v] ? u : v;
}
inline tuple<int, int, int> lca(int u, int v) {
if (u == v) return { 0, 0, u };
int U, V, p, iu = idx[u], iv = idx[v];
bool F = false;
if (iu > iv) swap(iu, iv), swap(u, v), F = true;
int k = __lg(iv - iu);
V = Min(st[k][iu + 1], st[k][iv - (1 << k) + 1]);
p = fa[V];
if (p == u) U = 0;
else {
int ip = idx[p];
k = __lg(iu - ip++);
U = Min(st[k][ip], st[k][iu - (1 << k) + 1]);
}
if (F) swap(U, V);
return { U, V, p };
} signed main() {
scanf("%d%d%d", &n, &q, &rt);
for (int i = 1, u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
e[u].emplace_back(v);
e[v].emplace_back(u);
}
dfs(rt);
for (int k = 1; k < lgN; ++k)
for (int i = 1; i + (1 << k) - 1 <= n; ++i)
st[k][i] = Min(st[k - 1][i], st[k - 1][i + (1 << (k - 1))]);
for (int u, v; q--; ) {
scanf("%d%d", &u, &v);
auto [U, V, p] = lca(u, v);
printf("%d\n", p);
fprintf(stderr, "u' = %d, v' = %d\n", U, V);
assert(!U || (idx[U] <= idx[u] && idx[u] <= R[U]));
assert(!V || (idx[V] <= idx[v] && idx[v] <= R[V]));
}
return 0;
}

给定 (u,v),如何 O(1) 求 lca(u,v) 的孩子 u',v',且分别为 u,v 的祖先或本身的更多相关文章

  1. 倍增 Tarjan 求LCA

                                                                                                         ...

  2. tarjan求lca的神奇

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  3. Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)

    题面 传送门 题目大意: 给定一个无向连通带权图G,对于每条边(u,v,w)" role="presentation" style="position: rel ...

  4. [总结]最近公共祖先(倍增求LCA)

    目录 一.定义 二.LCA的实现流程 1. 预处理 2. 计算LCA 三.例题 例1:P3379 [模板]最近公共祖先(LCA) 四.树上差分 1. 边差分 2. 点差分 3. 例题 一.定义 给定一 ...

  5. Tarjan算法离线 求 LCA(最近公共祖先)

    本文是网络资料整理或部分转载或部分原创,参考文章如下: https://www.cnblogs.com/JVxie/p/4854719.html http://blog.csdn.net/ywcpig ...

  6. 在线倍增法求LCA专题

    1.cojs 186. [USACO Oct08] 牧场旅行 ★★   输入文件:pwalk.in   输出文件:pwalk.out   简单对比时间限制:1 s   内存限制:128 MB n个被自 ...

  7. 树链剖分求LCA

    树链剖分中各种数组的作用: siz[]数组,用来保存以x为根的子树节点个数 top[]数组,用来保存当前节点的所在链的顶端节点 son[]数组,用来保存重儿子 dep[]数组,用来保存当前节点的深度 ...

  8. 树上倍增求LCA(最近公共祖先)

    前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...

  9. [算法]树上倍增求LCA

    LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...

  10. 全网最详系列之-倍增求LCA

    1,什么是LCA LCA.最近公共祖先.是一个在解决树上问题最强劲有力的一个工具.一般都是指.在一棵树上取两个节点a,b .另一个节点x它满足  x是a与b的祖先而且x深度最大.这个x就是节点a,b的 ...

随机推荐

  1. AI 大模型:现状、挑战与未来多维度发展趋势

    在科技浪潮的推动下,以 Deepseek 为代表的 AI 大模型正以颠覆性力量重塑产业格局.从金融风控到工业质检,从智慧医疗到智能教育,这些轻量化的 AI 工具不仅打破了传统工作模式的桎梏,更构建起一 ...

  2. idea 缺失右侧maven窗口

    最近整了一个别人的项目到本地,发现在git下载项目到本地后,再通过idea的打开项目后,缺失了右侧的maven窗口. 注: idea是有安装到maven(idea默认是已经安装好的) 打开的项目也是m ...

  3. 英语面试-Behavioral Question - second part

    前言 希望我总结的行为面试问题和答案能够给大家帮助. 学习方法:每个问题都有三部分组成. 第一部分是语料积累,这里是根据视频中的内容总结而来: 第二部分是中文描述,这里主要根据我自己的经历结合问题做出 ...

  4. 【Python】基础操作

    指定解释器的运行环境 有时候我们会遇见报错 SyntaxError: Non-ASCII character '\xe4' in file E:/PycharmProjects/LEDdisplay2 ...

  5. leetcode每日一题:最小化字符串长度

    题目 2716. 最小化字符串长度 给你一个下标从 0 开始的字符串 s ,重复执行下述操作 任意 次: 在字符串中选出一个下标 i ,并使 c 为字符串下标 i 处的字符.并在 i 左侧(如果有)和 ...

  6. 新装ubuntu电脑的一些调整

    必要命令的安装 必要开发工具的安装 更换国内软件源 /etc/apt/sources.list文件,后面添加下面地址用来添加阿里源 deb http://mirrors.aliyun.com/ubun ...

  7. idea git建立分支、切换分支、合并分支

    为什么要建立分支 git默认的主分支名字为master,一般团队开发时,都不会在master主分支上修改代码,而是建立新分支,测试完毕后,在将分支的代码合并到master主分支上 2.操作如下: 2. ...

  8. VSCode输出框中文乱码问题

    vscode输出中文的时候,总是出现乱码.找了一个一劳永逸解决问题的方法,转载的,原教程地址:https://blog.csdn.net/a19990412/article/details/90270 ...

  9. python,循环中通过判断条件中止循环的方法(braek 语句)

    直接对下方代码进行解析 for a in range(5): print(a) if a == 2: break 依次读取range内的数字,并答应出来.当读取出来的数字是2时,结束循环,执行后入下图 ...

  10. 什么是 MySQL 的主从同步机制?它是如何实现的?

    什么是 MySQL 的主从同步机制?它是如何实现的? MySQL 的主从同步机制是一种将主数据库(Master)上的数据实时或接近实时地同步到从数据库(Slave)的机制.通过这种机制,从数据库可以获 ...