两种lca的求法:树上倍增,tarjan
第一种:树上倍增
f[x,k]表示x的2^k辈祖先,即x向根结点走2^k步达到的结点。
初始条件:f[x][0]=fa[x]
递推式:f[x][k]=f[ f[x][k-1] ][k-1]
一次bfs预处理f数组(nlogn),然后每次询问都可以在(logn)时间内求出x,y的lca
求lca的步骤
1.令x的深度大于y,然后通过二进制拆分将x上调到与y同一个深度(依次用k=2^logn,...2^1,2^0试探)
2.如果此时x==y,那么y=lca(x,y),算法结束
3.继续用第一步的二进制拆分法同时将x,y往上提,保持深度一致,并且两者不可以相会
4.最后f[x][0]=f[y][0]=lca(x,y)
注意第三步,x,y不可以相会!
int f[maxn][],d[maxn],t;
void bfs(){
queue<int>q;
q.push();d[]=;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i!=-;i=edge[i].nxt){
int v=edge[i].to;
if(d[v])continue;
f[v][]=u;
d[v]=d[u]+;
for(int k=;k<=;k++)
f[v][k]=f[f[v][k-]][k-];
q.push(v);
}
}
}
int lca(int x,int y){
if(d[x]<d[y])swap(x,y);
for(int i=;i>=;i--)
if(d[f[x][i]]>=d[y])x=f[x][i];
if(x==y)return y;
for(int i=;i>=;i--)
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][];
}
第二种,tarjan+并查集优化,离线算法
首先说明最简单的向上标记法:x,y沿着边不停往上寻找lca,直到相遇,相遇点就是lca
使用并查集对“向上标记法”进行优化
在tarjan算法的dfs过程中,所有点分为三类
1.没有被访问到的点。这类点被标记0
2.正处于dfs阶段,即没有经过回溯的点,这类点给标记1
3.已经结束dfs阶段,即已经回溯结束的点,这类点给标记2
每次一个点结束dfs阶段,就将其标记位2,并将这个点合并到其父亲所在的并查集中(可以保证其父亲此时的标记一定1),
当其他点有问题边连到该点时,这个问题的解就是其所在并查集的标号
比较形象化的理解,在dfs过程中,x,y结点的lca必定是其路径上深度最小的结点,而深度最小的结点在dfs访问到y时一定没有被回溯,并且此时x已经被回溯了,被归并在lca所在的集合中,
那么扫描和y有关的所有问题,如果另一个点已经被标记位2,说明y和这个点已经相遇过了,相遇的点就是这个点所在的并查集标号
那么就使用并查集来优化这个集合的合并即可。
//多次求树上两点之间的距离
vector<int>q[maxn],q_id[maxn];//查询链表
int v[maxn],f[maxn],ans[maxn];//标记数组,并查集
void tarjan(int x){
v[x]=;
for(int i=head[x];i!=-;i=edge[i].nxt){
int y=edge[i].to
if(v[y])continue;
d[y]=d[x]+edge[i].w;//求y的深度
tarjan(y);
f[y]=x;//回溯后将y并入x集合中
}
for(int i=;i<q[x].size();i++){
int y=q[x][i],id=q_id[x][i];
if(v[y]==){//y是已经回溯过的结点
int lca=find(y);//使用并查集求y的集合标记
ans[id]=min(ans[id],d[x]+d[y]-*d[lca]);
}
}
v[x]=;//x回溯完成了
}
两种lca的求法:树上倍增,tarjan的更多相关文章
- 倍增 Tarjan 求LCA
...
- FJUT 聪明的商人(树上倍增)题解
思路:求树上两点的距离,显然是dep[u] + dep[v] - 2 * dep[lca],用树上倍增去写. 参考:树上倍增的写法和应用(详细讲解,新手秒懂) 代码: #include<set& ...
- LCA离线Tarjan,树上倍增入门题
离线Tarjian,来个JVxie大佬博客最近公共祖先LCA(Tarjan算法)的思考和算法实现,还有zhouzhendong大佬的LCA算法解析-Tarjan&倍增&RMQ(其实你们 ...
- 图论分支-倍增Tarjan求LCA
LCA,最近公共祖先,这是树上最常用的算法之一,因为它可以求距离,也可以求路径等等 LCA有两种写法,一种是倍增思想,另一种是Tarjan求法,我们可以通过一道题来看一看, 题目描述 欢乐岛上有个非常 ...
- 树上倍增求LCA及例题
先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...
- Codevs 2370 小机房的树 LCA 树上倍增
题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...
- [学习笔记] 树上倍增求LCA
倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...
- NOIP2013 货车运输 (最大生成树+树上倍增LCA)
死磕一道题,中间发现倍增还是掌握的不熟 ,而且深刻理解:SB错误毁一生,憋了近2个小时才调对,不过还好一遍AC省了更多的事,不然我一定会疯掉的... 3287 货车运输 2013年NOIP全国联赛提高 ...
- HDU 4822 Tri-war(LCA树上倍增)(2013 Asia Regional Changchun)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4822 Problem Description Three countries, Red, Yellow ...
随机推荐
- cookie——创建、获取、删除
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- pytroch 0.3 到 0.4版本迁移资料mark
搜了一堆,还是官方资料给力,一份中文,一份英文,maek一下 https://www.pytorchtutorial.com/pytorch-0-4-0-migration-guide/ https: ...
- SpringSecurity实现短信验证码登录(Token)
- shell-自动按省市建立文件夹,并在每个城市下创建当前日期文件夹
Code: #!/bin/bash date=`date +%Y%m%d` ProName="广东市" CityName="广州市 汕尾市 阳江市 揭阳市 茂名市 江门市 ...
- BIM 3D 数据交换格式 ----张建平(清华女)
1.collada EXPORTER 2.FBX 3D MAX 3.DAE 4.3D中的OBJ文件格式详解 ( http://www.cnblogs.com/slysky/p/408130 ...
- git与eclipse集成之代码冲突与解决
1.1. 代码冲突与解决 目前使用git管理代码,产生冲突的原因,主要是当多个人向特性分支提交代码时,如果两个人修改了同一个文件,第二个人提交代码时就可能会冲突. 举例说明: 创建远程特性分支.远程个 ...
- $Django 发送邮件--django封装模块和python内置SMTP模块
一 使用SMTP模块发送邮件 import smtplib from email.mime.text import MIMEText from email.header import Header m ...
- datepicker 属性设置 以及方法和事件
DatePicker支持鼠标点选日期,同时还可以通过键盘控制选择: page up/down - 上一月.下一月 ctrl+page up/down - 上一年.下一年 ctrl+home - 当前月 ...
- PHP随机红包算法
2017年1月14日 14:19:14 星期六 一, 整体设计 算法有很多种, 可以自行选择, 主要的"架构" 是这样的, 用redis decr()命令去限流, 用mysql去记 ...
- 转载:UML学习(三)-----序列图(silent)
原文:http://www.cnblogs.com/silent2012/archive/2011/09/14/2172219.html UML的模型中可分为两种,动态模型和静态模型.用例图.类图和对 ...