LCA的离线快速求法
最常见的LCA(树上公共祖先)都是在线算法,往往带了一个log。有一种办法是转化为“+-1最值问题”得到O(n)+O(1)的复杂度,但是原理复杂,常数大。今天介绍一种允许离线时接近线性求LCA的方法。
一个点和其他点的LCA必定是它到root路径上的所有节点之一,而另一个节点刚好在哪个节点下,LCA就是谁:
如图,标粗的箭头为当前搜索的路径,左边为已经搜索完毕的路径,右边的黑色节点尚未搜索。现在要求节点cur和节点a的LCA,显然a是什么颜色,LCA就也是这个颜色,如果a还没有被搜索到,那就不处理,把这个询问留给搜索到a的时候处理(那个时候cur肯定已经访问过了)。
那怎么做这个染色呢?我们对所有节点做一个并查集,每当一个节点搜索完毕,处理完了自己的答案,就把自己合并到父亲fa里面,那么在我搜完之后,父节点fa搜完之前,fa的其他所有儿子的公共祖先都是fa了:
当cur节点搜索完毕后,回到fa,讲cur修改为橙色并入到fa里(而且我们使用了并查集,此后查询cur的子节点也将得到fa),之后在fa搜索其他儿子节点时,他们和cur子树里的节点的LCA一定是fa,而当fa全部搜索完成后,他又被并入上级节点,以此类推,就可以在一遍dfs中就获取所有询问的答案。
参考代码:
int N, Q, p[MAX], qa[MAX], qb[MAX], ans[MAX];
vector<int> has[MAX];
struct ufs {
int in[MAX];
ufs() {
std::iota(in, in + N, 0);
}
void merge(int v, int u) { //! v合并给u
in[v] = u;
}
int find(int u) {
return in[u]==u ? u : (in[u] = find(in[u])); //! 带路径压缩
}
};
class Tree
{
std::vector<int> son[MAX];
ufs f;
void getans(int u) {
for (auto v: son[u]) {
getans(v); f.merge(v, u); //! 处理子树后,将其并入
}
for (auto i: has[u]) {
auto v (qa[i]^qb[i]^u); //! 该询问的另一个点
if (f.find(v) != v) ans[i] = f.find(v);
}
}
public:
#define root 0
Tree() {
for (int i = 1; i < N; ++i) son[p[i]].push_back(i);
getans(root);
}
#undef root
};
main() {
scanf("%d%d", &N, &Q);
for (int i = 1; i < N; ++i) scanf("%d", p + i);
for (int i = 0; i < Q; ++i) {
scanf("%d%d", qa + i, qb + i);
has[qa[i]].push_back(i);//! 把询问归到qa和qb下
has[qb[i]].push_back(i);
}
auto tr = new Tree;
for (int i = 0; i < Q; ++i)
printf("%d\n", ans[i]);
}
一个提交地址:https://judge.yosupo.jp/problem/lca
LCA的离线快速求法的更多相关文章
- (私人收藏)[开发必备]最全JQuery离线快速查找手册(可查询可学习,带实例)
[开发必备]最全JQuery离线快速查找手册(可查询可学习,带实例) https://pan.baidu.com/s/16bUd4iA3p0c5RHbzaC60IQe4zh
- (私人收藏)[开发必备]最全Java离线快速查找手册(可查询可学习,带实例)
(私人收藏)[开发必备]最全Java离线快速查找手册(可查询可学习,带实例) https://pan.baidu.com/s/1L54VuFwCdKVnQGVc8vD1TQnwmj java手册 Ja ...
- 学习笔记--最近公共祖先(LCA)的几种求法
前言: 给定一个有根树,若节点\(z\)是两节点\(x,y\)所有公共祖先深度最大的那一个,则称\(z\)是\(x,y\)的最近公共祖先(\(Least Common Ancestors\)),简称\ ...
- poj 1986 Distance Queries(LCA:倍增/离线)
计算树上的路径长度.input要去查poj 1984. 任意建一棵树,利用树形结构,将问题转化为u,v,lca(u,v)三个点到根的距离.输出d[u]+d[v]-2*d[lca(u,v)]. 倍增求解 ...
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1272 Solved: 451[Submit][Status ...
- poj 1330 LCA (倍增+离线Tarjan)
/* 先来个倍增 */ #include<iostream> #include<cstring> #include<cstdio> #define maxn 100 ...
- hdu2586How far away ?(LCA LCATarjan离线)
题目链接:acm.hdu.edu.cn/showproblem.php?pid=2586 题目大意:有n个点,同n-1条带有权值的双向边相连,有m个询问,每个询问包含两个数x,y,求x与y的最短距离. ...
- POJ 1470 Closest Common Ancestors (LCA,离线Tarjan算法)
Closest Common Ancestors Time Limit: 2000MS Memory Limit: 10000K Total Submissions: 13372 Accept ...
- 最近公共祖先LCA Tarjan 离线算法
[简介] 解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...
随机推荐
- Hibernate学习一:Hebinate入门以及一些小问题
1:Hebinate框架的简述: Hebinate框架主要用用在javaee开发中的dao层设计,实现对数据库的crud等操作, Hibernate的底层通过jdbc实现,通过对jdbc的封装,实现对 ...
- JSP和Servlet有哪些相同点和不同点?
JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达.JSP编译后是"类servlet". Servlet和JSP最主要的不同点在于,Servl ...
- python模块相互依赖的解决方案
第一种:将相互依赖的文件中的其中一个文件的代码移植到另一个文件中... 第二种:将 import .... 或 from ... import 语句的位置移动到def函数内部,由于import和fro ...
- css添加全部省略号的方式
https://css-tricks.com/line-clampin/ 框架Clamp:https://github.com/josephschmitt/Clamp.js
- redis支持哪些数据类型?redis命令大全
一.redis支持的数据类型 1)String 常用命令:set/get/decr/incr/mget等: 应用场景:String是最常用的一种数据类型,普通的key/value存储都可以归为此类: ...
- springboot-@EventListener简单用法
@EventListener简单描述 简化我们编写监听类的步骤,不需要再继承ApplicationListener接口去实现onApplicationEvent了. 例子: @Component pu ...
- 如何通过HibernateDaoSupport将Spring和Hibernate 结合起来?
用 Spring 的 SessionFactory 调用 LocalSessionFactory.集成过程分三步: 配置 the Hibernate SessionFactory. 继承 Hibern ...
- 转:C++经典排序算法总结
转载至:https://www.cnblogs.com/fnlingnzb-learner/p/9374732.html 一.算法概述 0.1 算法分类 十种常见排序算法可以分为两大类: 非线性时间比 ...
- 转载:平衡二叉树(AVL Tree)
平衡二叉树(AVL Tree) 转载至:https://www.cnblogs.com/jielongAI/p/9565776.html 在学习算法的过程中,二叉平衡树是一定会碰到的,这篇博文尽可能简 ...
- Linux中一切皆文件
谈一谈Linux中一切皆文件 1. Linux中所有内容都是以文件的形式保存和管理,即:一切皆文件. 普通文件是文件. 目录(在win下称为文件夹)是文件. 硬件设备(键盘.硬盘.打印机)是文件. 套 ...