LCA模板 ( 最近公共祖先 )
LCA 有几种经典的求取方法、这里只给出模板,至于原理我完全不懂。
1、RMQ转LCA、复杂度O(n+nlog2n+m)
大致就是 DFS求出欧拉序 => 对欧拉序做ST表 => LCA(u, v) 即为 u、v 最先出现在欧拉序中的编号之间的最小值。
因为 LCA 的子树中必定有一个节点是 u,一个是 v,而且必定在两个节点到根节点的唯一路径上。
例如有欧拉序列 1 2 1 3 4 3 1 则 LCA(2, 3) == 1 、首次出现 2 的下标是 2、首次出现 3 的下标是 4、则 LCA 就是下标 2~4 之间的最小值即 1
#include<bits/stdc++.h>
using namespace std;
;///顶点数
];
int Head[maxn], cnt;
int n, q, s;///点数、问询数、dfs起点
int fp[maxn]; int dfsLen;///每个顶点在欧拉序中第一次出现的位置、DFS序的长度(用做时间戳)
int id[maxn]; int idLen;///每个顶点在DFS序中访问次序(用于离散化)、id数组的长度
][];///跑ST表的dp数组(第二维应开到 ceil(log2(maxn<<1)) )
inline void init()
{
memset(dp, , sizeof(dp));
memset(Head, -, sizeof(Head));
cnt = ; idLen = dfsLen = ;
}
inline void AddEdge(int from, int to, int weight)
{
Edge[cnt].v = to;
Edge[cnt].nxt = Head[from];
Head[from] = cnt++;
}
void dfs(int v, int Fa)
{
int tmp;
id[tmp = ++idLen] = v;
dp[fp[v] = ++dfsLen][] = tmp;
; i=Edge[i].nxt){
int Eiv = Edge[i].v;
if(Eiv == Fa) continue;
dfs(Eiv, v, d);
dp[++dfsLen][] = tmp;
}
}
void GetST()
{
; (<<j)<=dfsLen; j++){
; i+(<<j)-<=dfsLen; i++){
dp[i][j] = min(dp[i][j-], dp[i+(<<(j-))][j-]);
}
}
}
int LCA(int u, int v)
{
if(fp[u] > fp[v]) swap(u, v);
int L = fp[u], R = fp[v];
)/log());
<<k)+][k])];
}
int main(void)
{
scanf("%d %d %d", &n, &s, &q);
; i<n; i++){
int u, v;
scanf("%d %d", &u, &v);
AddEdge(u, v);
AddEdge(v, u);
}
dfs(s, -);
GetST();
while(q--){
int u, v;
scanf("%d %d", &u, &v);
printf("%d\n", LCA(u, v));
}
;
}
2、树上倍增 、复杂度 O(n+nlog2maxDep+mlog2maxDep), maxDep在最坏情况下等于n
大致就是 DFS求出每个节点的深度、父亲节点这两个信息 => 通过倍增求出每个节点向根节点方向走 2^j 所能到达节点是什么
=> LCA(u, v) 就可以通过预先处理好的倍增数组先移动u、v使其深度一样、然后再一起 2^j 向上跳,直到跳转到 LCA
#include<bits/stdc++.h>
using namespace std;
;///顶点的数目
];
int Head[maxn], cnt;
int dep[maxn], maxDep;///每个点的深度、最深点的深度
];///倍增记录数组( Fa[i][j] 从 i 号节点开始走 2^j 步能到达的点 )
int n, m, s, q;///点、边、dfs起点、问询数
inline void init()
{
memset(Head, -, sizeof(Head));
memset(Fa, -, sizeof(Fa));
cnt = ; maxDep = ;
}
inline void AddEdge(int from, int to)
{
Edge[cnt].v = to;
Edge[cnt].nxt = Head[from];
Head[from] = cnt++;
}
void dfs(int v)
{
] != -)
maxDep = max(maxDep, dep[v] = dep[Fa[v][]]+);
; i=Edge[i].nxt){
int Eiv = Edge[i].v;
]) continue;
Fa[Eiv][] = v;
dfs(Eiv);
}
}
inline void Doubling()
{
));
; j<=UP; j++){
; i<=n; i++){
] != -)
Fa[i][j] = Fa[Fa[i][j-]][j-];
}
}
}
int LCA(int u, int v)
{
));
if(dep[u] < dep[v]) swap(u, v);
; j--)
&& dep[Fa[u][j]] >= dep[v])
u = Fa[u][j];
if(u == v) return v;
; j--){
if(Fa[u][j] != Fa[v][j]){
u = Fa[u][j];
v = Fa[v][j];
}
}
];
}
int main(void)
{
scanf("%d %d %d", &n, &s, &q);
; i<n; i++){
int u, v;
scanf("%d %d", &u, &v);
AddEdge(u, v);
AddEdge(v, u);
}
dfs(s);
Doubling();
while(q--){
int u, v;
scanf("%d %d", &u, &v);
printf("%d\n", LCA(u, v));
}
;
}
3、Tarjan算法( 离线 )、复杂度 O(n+m+nα(n))
大致就是 算了给个链接吧 Tarjan求LCA
#include<bits/stdc++.h>
using namespace std;
;
];
struct Query{ int v, id;
Query(){};
Query(int _v, int _id):v(_v),id(_id){};
}; vector<Query> q[maxn];
int Head[maxn], cnt;
int Fa[maxn];///并查集数组
int ans[maxn];///问询数数组大小要注意一下、不一定是 maxn
bool vis[maxn];///Tarjan算法中的标记数组
int n, m, s, qNum;///点、边、Tarjan递归起点、问询数
inline void init()
{
memset(Head, -, sizeof(Head));
memset(vis, false, sizeof(vis));
cnt = ;
}
inline void AddEdge(int from, int to)
{
Edge[cnt].v = to;
Edge[cnt].nxt = Head[from];
Head[from] = cnt++;
}
int Findset(int x)
{
int root = x;
while(Fa[root] != root) root = Fa[root];
int tmp;
while(Fa[x] != root){
tmp = Fa[x];
Fa[x] = root;
x = tmp;
}
return root;
}
void Tarjan(int v, int f)
{
Fa[v] = v;
; i=Edge[i].nxt){
int Eiv = Edge[i].v;
if(Eiv == f) continue;
Tarjan(Eiv, v);
Fa[Findset(Eiv)] = v;
}
vis[v] = true;
; i<q[v].size(); i++){
if(vis[q[v][i].v])
ans[q[v][i].id] = Findset(q[v][i].v);
}
}
int main(void)
{
init();
scanf("%d %d %d %d", &n, &m, &s, &qNum);
; i<=m; i++){
int u, v;
scanf("%d %d", &u, &v);
AddEdge(u, v);
AddEdge(v, u);
}
; i<q; i++){
int u, v;
scanf("%d %d", &u, &v);
q[u].push_back(Query(v, i));
q[v].push_back(Query(u, i));
}
Tarjan(s, -);
; i<q; i++) printf("%d\n", ans[i]);
;
}
4、树链剖分
并不会树剖,以后再补......
LCA模板 ( 最近公共祖先 )的更多相关文章
- HDU 2586 How far away ?(LCA模板 近期公共祖先啊)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the vi ...
- 【LCA求最近公共祖先+vector构图】Distance Queries
Distance Queries 时间限制: 1 Sec 内存限制: 128 MB 题目描述 约翰的奶牛们拒绝跑他的马拉松,因为她们悠闲的生活不能承受他选择的长长的赛道.因此他决心找一条更合理的赛道 ...
- CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先)
CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先) 题意分析 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天, ...
- 算法模板——LCA(最近公共祖先)
实现的功能如下——在一个N个点的无环图中,共有N-1条边,M个访问中每次询问两个点的距离 原理——既然N个点,N-1条边,则说明这是一棵树,而且联通.所以以1为根节点DFS建树,然后通过求两点的LCA ...
- [模板] 最近公共祖先/lca
简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...
- 【洛谷 p3379】模板-最近公共祖先(图论--倍增算法求LCA)
题目:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 解法:倍增. 1 #include<cstdio> 2 #include<cstdlib> 3 #include ...
- LCA——求解最近公共祖先
LCA 在有根树中,两个节点 u 和 v 的公共祖先中距离最近的那个被称为最近公共祖先(LCA,Lowest Common Ancestor). 有多种算法解决 LCA 或相关的问题. 基于二分搜索的 ...
- LCA(最近公共祖先)——离线 Tarjan 算法
tarjan算法的步骤是(当dfs到节点u时):1 在并查集中建立仅有u的集合,设置该集合的祖先为u1 对u的每个孩子v: 1.1 tarjan之 1.2 合并v到父节点u的集合,确保集合的祖 ...
- 倍增法求lca(最近公共祖先)
倍增法求lca(最近公共祖先) 基本上每篇博客都会有参考文章,一是弥补不足,二是这本身也是我学习过程中找到的觉得好的资料 思路: 大致上算法的思路是这样发展来的. 想到求两个结点的最小公共祖先,我们可 ...
随机推荐
- mysql——触发器——示例
数据准备: ), d_id ), name ), age ), sex ), homeadd ) ); ,,,'nan','beijing'); ,,,'nv','hunan'); ,,,'nan', ...
- springboot基于方法级别注解事务的多数据源切换问题
springBoot多数据源配置 配置读数据源 @Component @ConfigurationProperties(prefix = "jdbc.read") @Propert ...
- Vector和ArrayList的区别联系,Hashtable和HashMap的区别联系
Vector.Hashtable是早期的集合类,线程安全,但是效率低下,被相同原理.结构的ArrayList.HashMap取代. 1.Vector和ArrayList的区别和联系: 联系:实现原理相 ...
- 使用new关键字创建对象数组(C#,C++,Java)
今天遇到一个题目 分析下面的代码,判断代码是否有误. using System; namespace Test1 { class Point { public int x; public int y; ...
- 通过U盘或CD/DVD装centos7,出现“dracut-initqueue timeout..."解决办法
1.在用CD/DVD挂载centos7镜像安装系统时,出现“dracut-initqueue timeout...", :/# cd dev :/# ls 2.这是因为安装程序未能找到安装文 ...
- 思考--PostgreSQL在与mysql的比较中稍微弱势项
PostgreSQL在与mysql的比较中稍微弱势项: 1.都是堆表,没有所谓的聚集索引表,其实问题不大,聚集索引表也只是在使用聚集索引那些列有加速,而且pg也有聚集索引,只不过要定期重建. 2.mv ...
- PHP 模拟http 请求
php 模拟请求类 <?php /** * fangdasheng * http 模拟请求 */ class Myhttp { private $apiUrl; // 构造函数 public f ...
- uoj218_火车管理
题意 \(n\)个位置,每个位置一个栈,三种操作,询问区间栈顶的和,区间入栈某个数,单点出栈某个数. 分析 用一个线段树来维护栈顶的和,区间(单点)更新和区间询问. 用一个主席树来维护每个位置最新一次 ...
- semantic-ui的表单使用
semantic-ui 的表单使用 最近找了一款ui库,jquery可以使用的.可以进行个性化定制,感觉还不错. 现状 简单阐述下该ui的现状吧,目前止步于2.4的版本,github商讨了一波.大致是 ...
- RabbitMQ入门教程(十):队列声明queueDeclare
原文:RabbitMQ入门教程(十):队列声明queueDeclare 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https:// ...