LCA的一些算法
LCA,就是求树上任意两点的最近公共祖先
(本题图片与代码均为Luogu3379)
方法我好像讲过一个,这次把主要的三个一起讲一讲
<1> 倍增(O(n log n))
我们先考虑最基本的LCA,记录每一个点的父节点和深度。
对于两个点x,y,先将它们调到同一高度(令dep[x]>dep[y],即把x向上移(dep[x]-dep[y])步即可,然后一起往上走就可以了。
这复杂度是O(nq)的,所以在此基础上优化。
用father[i][j]表示点j向上走2^i步时的点是多少(没有就是-1),然后每次上移只要走log n次即可。
预处理的话 father[i][j]]=father[i-1][father[i-1][j]];递推即可。
CODE
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=,P=;
struct data
{
int to,next;
}e[N*+];
int head[N*+],dep[N],father[P][N],i,j,n,m,x,y,root,k;
inline void read(int &x)
{
x=; char ch=getchar();
while (ch<''||ch>'') ch=getchar();
while (ch>=''&&ch<='') x=x*+ch-'',ch=getchar();
}
inline void write(int x)
{
if (x/) write(x/);
putchar(x%+'');
}
inline void add(int x,int y)
{
e[++k].to=y; e[k].next=head[x]; head[x]=k;
}
inline void dfs(int k,int fa,int d)
{
father[][k]=fa;
dep[k]=d;
for (int i=head[k];i!=-;i=e[i].next)
if (e[i].to!=fa) dfs(e[i].to,k,d+);
}
inline int LCA(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (i=P-;i>=;--i)
if (((dep[x]-dep[y])>>i)&) x=father[i][x];
if (x==y) return x;
for (i=P-;i>=;--i)
if (father[i][x]!=father[i][y])
{
x=father[i][x];
y=father[i][y];
}
return father[][x];
}
int main()
{
read(n); read(m); read(root);
memset(e,-,sizeof(e));
memset(head,-,sizeof(head));
memset(father,-,sizeof(father));
for (i=;i<n;++i)
{
read(x); read(y);
add(x,y); add(y,x);
}
dfs(root,-,);
for (j=;j<P-;++j)
for (i=;i<=n;++i)
if (father[j][i]==-) father[j+][i]=-; else father[j+][i]=father[j][father[j][i]];
while (m--)
{
read(x); read(y);
write(LCA(x,y));
putchar('\n');
}
return ;
}
<2> DFS序+RMQ(O(n log n))
考虑一棵树的DFS序(不懂查百度),如样例的DFS序就是:4 2 4 1 3 1 5 1 4
不难发现,两个点的LCA就是它们第一次出现的位置之间深度最小的点(证明略)。
所以我们用RMQ实现O(1)查询,预处理是O(n log n)
注意RMQ返回的是具体的点而不是深度
不得不提的是数据的first[x],first[y]值不一定是从小到大排的,需要判断一下(RE了很久)
CODE
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
const int N=,P=;
struct data
{
int to,next;
}e[N*+];
struct RMQ
{
int num,x;
}f[P][N*+];
int head[N],first[N],n,m,root,i,j,k,tot,x,y;
inline void read(int &x)
{
x=; char ch=getchar();
while (ch<''||ch>'') ch=getchar();
while (ch>=''&&ch<='') x=x*+ch-'',ch=getchar();
}
inline void write(int x)
{
if (x/) write(x/);
putchar(x%+'');
}
inline void add(int x,int y)
{
e[++k].to=y; e[k].next=head[x]; head[x]=k;
}
inline void dfs(int k,int fa,int d)
{
f[][++tot].num=k;
f[][tot].x=d;
first[k]=tot;
for (int i=head[k];i!=-;i=e[i].next)
if (e[i].to!=fa) dfs(e[i].to,k,d+),f[][++tot].num=k,f[][tot].x=d;
}
int main()
{
//freopen("testdata.in","r",stdin); freopen("testdata.out","w",stdout);
read(n); read(m); read(root);
memset(e,-,sizeof(e));
memset(head,-,sizeof(head));
for (i=;i<n;++i)
{
read(x); read(y);
add(x,y); add(y,x);
}
dfs(root,-,);
for (j=;j<P;++j)
for (i=;i+(<<j)-<=tot;++i)
if (f[j-][i].x<f[j-][i+(<<(j-))].x) f[j][i].x=f[j-][i].x,f[j][i].num=f[j-][i].num; else f[j][i].x=f[j-][i+(<<(j-))].x,f[j][i].num=f[j-][i+(<<(j-))].num;
while (m--)
{
read(x); read(y);
x=first[x]; y=first[y];
if (x>y) swap(x,y);
int k=(int)log2(y-x+);
if (f[k][x].x<f[k][y-(<<k)+].x) write(f[k][x].num); else write(f[k][y-(<<k)+].num);
putchar('\n');
}
return ;
}
<3> Tarjan(O(n+q))
这个我第一次写这道题的时候已经写过了,具体看http://www.cnblogs.com/cjjsb/p/8203882.html
这里给一下邻接表的代码(好不容易会打)
CODE
#include<cstdio>
#include<cstring>
using namespace std;
const int N=;
struct data
{
int to,next;
}e[N*+];
struct ques
{
int to,next,num;
}q[N*+];
int head[N],qhead[N],father[N],ans[N],i,k,qk,x,y,n,m,root;
bool vis[N];
inline void read(int &x)
{
x=; char ch=getchar();
while (ch<''||ch>'') ch=getchar();
while (ch>=''&&ch<='') x=x*+ch-'',ch=getchar();
}
inline void write(int x)
{
if (x/) write(x/);
putchar(x%+'');
}
inline void add(int x,int y)
{
e[++k].to=y; e[k].next=head[x]; head[x]=k;
}
inline void qadd(int x,int y,int z)
{
q[++qk].to=y; q[qk].num=z; q[qk].next=qhead[x]; qhead[x]=qk;
}
inline int getfather(int k)
{
return father[k]==k?k:father[k]=getfather(father[k]);
}
inline void dfs(int k)
{
vis[k]=;
for (int i=qhead[k];i!=-;i=q[i].next)
if (vis[q[i].to]) ans[q[i].num]=getfather(q[i].to);
for (int i=head[k];i!=-;i=e[i].next)
if (!vis[e[i].to]) dfs(e[i].to),father[e[i].to]=k;
}
int main()
{
read(n); read(m); read(root);
memset(head,-,sizeof(head));
memset(qhead,-,sizeof(qhead));
memset(e,-,sizeof(e));
memset(q,-,sizeof(q));
for (i=;i<n;++i)
{
read(x); read(y);
add(x,y); add(y,x);
}
for (i=;i<=m;++i)
{
read(x); read(y);
qadd(x,y,i); qadd(y,x,i);
}
for (i=;i<=n;++i)
father[i]=i;
dfs(root);
for (i=;i<=m;++i)
write(ans[i]),putchar('\n');
return ;
}
LCA的一些算法的更多相关文章
- hdu 3078(LCA的在线算法)
Network Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Sub ...
- 最近公共祖先(LCA)---tarjan算法
LCA(最近公共祖先).....可惜我只会用tarjan去做 真心感觉tarjan算法要比倍增算法要好理解的多,可能是我脑子笨吧略略略 最近公共祖先概念:在一棵无环的树上寻找两个点在这棵树上深度最大的 ...
- POJ 1470 Closest Common Ancestors (最近公共祖先LCA 的离线算法Tarjan)
Tarjan算法的详细介绍,请戳: http://www.cnblogs.com/chenxiwenruo/p/3529533.html #include <iostream> #incl ...
- 【HDU 4547 CD操作】LCA问题 Tarjan算法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4547 题意:模拟DOS下的cd命令,给出n个节点的目录树以及m次查询,每个查询包含一个当前目录cur和 ...
- 【HDU 2586 How far away?】LCA问题 Tarjan算法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 题意:给出一棵n个节点的无根树,每条边有各自的权值.给出m个查询,对于每条查询返回节点u到v的最 ...
- 【POJ 1330 Nearest Common Ancestors】LCA问题 Tarjan算法
题目链接:http://poj.org/problem?id=1330 题意:给定一个n个节点的有根树,以及树中的两个节点u,v,求u,v的最近公共祖先. 数据范围:n [2, 10000] 思路:从 ...
- LCA的倍增算法
LCA,即树上两点之间的公共祖先,求这样一个公共祖先有很多种方法: 暴力向上:O(n) 每次将深度大的点往上移动,直至二者相遇 树剖:O(logn) 在O(2n)预处理重链之后,每次就将深度大的沿重链 ...
- LCA(倍增在线算法) codevs 2370 小机房的树
codevs 2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点, ...
- POJ1470Closest Common Ancestors 最近公共祖先LCA 的 离线算法 Tarjan
该算法的详细解释请戳: http://www.cnblogs.com/Findxiaoxun/p/3428516.html #include<cstdio> #include<alg ...
- 最近公共祖先LCA Tarjan 离线算法
[简介] 解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...
随机推荐
- 自己来实现一套IOC注解框架
我们自己来实现一套IOC注解框架吧,采用的方式反射加注解和Xutils类似,但我们尽量不写那么麻烦,也不打算采用动态代理,我们扩展一个检测网络的注解,比如没网的时候我们不去执行方法而是给予没有网络的提 ...
- tinymce4.x 上传本地图片(自己写个插件)
tinymce是一款挺不错的html文本编辑器.但是添加图片是直接添加链接,不能直接选择本地图片. 下面我写了一个插件用于直接上传本地图片. 在tinymce的plugins目录下新建一个upload ...
- Spring Boot (#1 quick start)
Spring Boot (#1 quick start) 官方文档 Spring Boot是为了简化Spring应用的创建.运行.调试.部署等而出现的,使用它可以做到专注于Spring应用的开发,而无 ...
- MSSQL段落还原脚本
--段落还原:数据库损坏范围比较大,跨多个数据文件甚至跨文件组的时候,我们不得不恢复整个数据库.--这时如果数据库特别大,数据库恢复时间将会很长.但我们可以使用SQL Server提供的段落还原,来逐 ...
- HTML table固定表头
最近尝试了几种HTML的table固定表头的方法..额...各有利弊,但很尴尬..... 1.thead和tbody的display设置为block; 这种可以实现,但是需要提前设置好每个th和td的 ...
- 纯Css实现Div高度根据自适应宽度(百分比)调整
在如今响应式布局的要求下,很多能自动调整尺寸的元素能够做到高宽自适应,如img,通过{width:50%;height:auto;}实现图片高度跟随宽度比例调整. 然而,用的最多的标签一哥Div却不能 ...
- Hsqldb中设置主键,并让主键自增
CREATE TABLE userinfo ( Id INTEGER GENERATED BY DEFAULT AS IDENTITY, Name varchar(100) NOT NULL, Dep ...
- MySQL索引选择不正确并详细解析OPTIMIZER_TRACE格式
一 表结构如下: CREATE TABLE t_audit_operate_log ( Fid bigint(16) AUTO_INCREMENT, Fcreate_time int(10) un ...
- Linux 进程管理之四大名捕
本文来自网络,著作权归原创作者所有,不做任何商业用途. 一.四大名捕 四大名捕,最初出现于温瑞安创作的武侠小说,是朝廷中正义力量诸葛小花的四大徒弟,四人各怀绝技,分别是轻功暗器高手“无情”.内功卓越的 ...
- 中断标志位 IRQF_ONESHOT
one shot本身的意思的只有一次的,结合到中断这个场景,则表示中断是一次性触发的,不能嵌套.对于primary handler,当然是不会嵌套,但是对于threaded interrupt han ...