利用Tarjan算法(LCA)求树上任意两点的距离

16 篇文章 0 订阅
订阅专栏
14 篇文章 0 订阅
订阅专栏

需求

给出 n 个点的一棵树 (无向边)

多次询问树上两点之间的最短距离

点的编号是1-n

思路

分析题意

设两个点分别为 x y

两个点到根节点的距离预处理到数组中 用 d[x] d[y] 表示

分析发现 树上两个点的距离可以用公式表示为

d[x] + d[y] - 2 * d[ LCA(x,y) ]

其中LCA(x,y) 表示点x和点y的最近公共祖先

红色路径表示d[x]  绿色路径表示d[y]

Tarjin算法求最近公共祖先(LCA)

基于深度优先遍历 把所有点分为三大类

第一类是已经被遍历过且回溯的点 在当前路径的左侧 绿色

第二类是正在搜索的点 从根节点到当前节点的路径 红色

第三类是还未搜索到的点 在当前路径的右侧 蓝色

思考

我们发现 图中被绿色线条框住的三个点(1,2,3)

点x的最近公共祖先是相同的点(在图中已经标识为LCA

所以 我们将(1,2,3)这三个点看作一个小子树 (利用并查集)

并用LCA标识点代表这个子树

以后的查询若涉及该子树中的点

答案即为LCA标识点

算法步骤

对于当前遍历的这个点(设为x)

查询与点x相关的所有询问

如果询问中的另一个点y(ex.询问为求点x与点y的距离处于绿色部分 也就是第一类

那么最近公共祖先就是点y所在并查集中的代表元素

对于已经遍历过的小子树(第一类)

可以用当前路径上(第二类)的某一个点代表它

AC代码(含详细注释)

#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int, int>PII;
const int N = 2e4 + 10;
const int M = N * 2;//无向边 边数两倍
int h[N], e[M], w[M], ne[M], idx;
int dist[N];//每个点与根节点的距离
int p[N];
int st[N];
int n, m;
int res[N];//存每一次询问的结果
vector<PII>query[N];// first存查询的另外一个点 second存查询编号

void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int fa)
{
	for (int i = h[u]; ~i; i = ne[i])
	{
		//当前到达的时点j
		int j = e[i];
		//树上DFS确保搜索的单向性
		//只从上往下搜索
		if (j == fa)continue;
		dist[j] = dist[u] + w[i];
		dfs(j, u);
	}
}

int find(int x)
{
	return x == p[x] ? x : find(p[x]);
}

//在线算法:读一个询问,处理一个,输出一个
//离线算法:读完全部询问,再全部处理完,再全部输出
//O(n+m)离线求LCA
void tarjan(int u)
{
	//2表示该点处于当前路径
	st[u] = 2;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!st[j])
		{
			tarjan(j);
			//并查集的合并 形成小子树
			p[j] = u;
		}
	}

	for (auto it : query[u])
	{
		int y = it.first, id = it.second;
		if (st[y] == 1)//如果询问的另一个点已经被遍历过了
		{
			//获取其所在并查集的代表元素 即为LCA点
			int anc = find(y);
			res[id] = dist[u] + dist[y] - dist[anc] * 2;
		}
	}

	//1表示该点处于第一类点 已经搜索完毕且回溯
	st[u] = 1;
}

int main()
{
	//初始化邻接表头节点
	memset(h, -1, sizeof h);

	scanf("%d%d", &n, &m);

	//存树
	for (int i = 0; i < n - 1; i++)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c), add(b, a, c);
	}

	//存询问
	for (int i = 0; i < m; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		//对于点a我们要查询与点b之间的距离
		//这是第i次询问
		if (a != b)//相同的两个点距离为0
		{
			query[a].push_back({ b,i });
			//要查询的另一个点可能不在第一类点
			//所以要双向记录查询要求
			//当另一个点作为"当前遍历点"时 可以查到答案
			query[b].push_back({ a,i });
		}
	}

	//并查集初始化
	for (int i = 1; i <= n; i++)p[i] = i;

	//DFS预处理每一个点到根节点的距离 dist数组
	dfs(1, -1);

	//tarjan算法也是一种DFS
	tarjan(1);

	for (int i = 0; i < m; i++)printf("%d\n", res[i]);

	return 0;
}

树上距离
shenyuedong123的博客
07-03 722
给定一棵树,询问树上两个点之间距离。 #include #include #include #include using namespace std; struct node { int v,c; }; const int maxn=10002; const int maxk=18; vectortree[maxn]; int d[maxn][maxk]; int eluer[maxn*2
tarjan算法各种应用
zyhyz的博客
07-17 1320
Robert Tarjan,一个很牛逼的计算机科学家。 tarjan算法真的是一个神奇的算法,一个简单的dfs却可以解决联通性的问题以及最近公共祖先。 首先介绍一下什么是强连通分量。 强连通:在一个有向,对于任意两点a,b都互相可达,则称这个是强连通的。 强连通分量:有向的极大强连通子。 先去健身。。。。剩下的回头补...
BZOJ1316 树上的询问(点分治)
riba2534的博客
10-15 418
题目描述: 一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 输入描述: 第一行两个整数n, p分别表示点的个数和询问的个数. 接下来n-1行每行三个数x, y, c,表示有一条树边x→y,长度为c. 接下来p行每行一个数Len,表示询问树中是否存在一条长度为Len的路径. 输出描述: 输出有p行,Yes或No. 样例输入:...
12. 树上两点距离
最新发布
jyhjsaq1281的博客
08-09 301
时间限制: 200ms空间限制: 32768kB。
树上任意两点距离
weixin_30731287的博客
08-28 897
// 任意两点间有唯一路径的无向是树 //HDU 6446 Tree and Permutation Time Limit: 2000/1000 MS (Java/Others)Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1192Accepted Submission(s): 4...
HDU 2376(树上任意2点间的距离)
qwesde
07-20 1121
题目链接 题意:树上任意2点之间距离的期望 dfs跑一遍,统计每条边的贡献#include<bits/stdc++.h> using namespace std;#define cl(a,b) memset(a,b,sizeof(a)) #define LL long longconst int maxn = 11005; const int inf = 1 << 23;struct Edg
hdu 2376(树上任意两点之间距离之和的平均值)
weixin_34071713的博客
06-03 247
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2376 思路: 引:如果暴力枚举两点距离是显然会超时的。转换一下思路,我们可以对每条边,所有可能的路径经过此边的次数:设这条边两端的点数分别为A和B,那 么这条边被经过的次数就是A*B,它对总的距离和的贡献就是(A*B*此边长度)。我们把所有边的贡献总和,再除以总路径数N*(N-1)/2,即...
Tarjan 离线LCA
weixin_34365417的博客
06-13 158
#include<bits/stdc++.h> using namespace std; const int maxn=450005; struct node{ int nxt; int to; }tree[maxn*2]; struct node1{ int LCA; int nxt; int to; }qtree[m...
contest_tarjan_LCA_源码
10-03
在查询阶段,只需要根据预处理得到的数据结构,就能快速找到任意两个节点的LCA。 在提供的压缩包中,5.cpp应该是实现tarjan LCA算法C++源代码,而5.exe则是编译后的可执行文件。通过分析源代码,我们可以更深入地...
支配树 与 tarjan算法
热门推荐
我数学不好
11-19 1万+
简介什么是支配树?支配树是什么?XD 对于一张有向(可以有环)我们规定一个起点r(为什么是r呢?因为网上都是这么规定的),从r点到上另一个点w可能存在很多条路径(下面将r到w简写为r->w)。 如果对于r->w的任意一条路径中都存在一个点p,那么我们称点p为w的支配点(当然这也是r->w的必经点),注意r点不讨论支配点。下面用idom[u]表示离点u最近的支配点。 对于原上除r外每一个点
Tarjan三大算法
爱玲姐姐的博客
03-31 3512
Robert Endre Tarjan是一个美国计算机学家,他传奇的一生中发明了无数算法,统称为Tarjan算法。其中最著名的有三个,分别用来解 1)有向的强连通分量 2) 无向的双联通分量 3) 最近公共祖先问题 一:有向的强连通分量 算法介绍(摘自百度百科) 如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected)。如...
有向中的Tarjan算法个人理解
qq_69908563的博客
01-15 418
tarjan,缩点,强连通分量。
最近公共祖先LCA(Tarjan算法)的思考和算法实现
diaoluo1817的博客
10-04 673
LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ)       //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下!       //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将...
树上距离(树形dp)
VUno)UKGzseI
04-21 1429
树上距离(树形dp) 题目描述 懒惰的温温今天上班也在偷懒。盯着窗外发呆的温温发现,透过窗户正巧能看到一棵n个节点的树。一棵n个节点的树包含n-1条边,且n个节点是联通的。树上两点之间的距离两点之间的最短路径包含的边数。 突发奇想的温温想要知...
树形DP--树上任意两点距离
weixin_30642561的博客
08-28 871
例题:HDU2376 HDU6446(2018CCPC网络赛) 思路:任意两点距离和可以转换为->路径长度乘经过路径次数的和。 经过次数:设这条边两端的点,被经过的次数分别为A和B,那么这条边被经过的次数就是A*B,它对总距离和的贡献就是(A*B*此边长度)。 每条边两端点经过次数的计算,可以用一次dfs解决。 任取一点为根,在dfs的过程中,对每个点k记录其子树包含的点数...
Tarjan算法LCA(最近公共祖先)
爱.NET
08-03 205
LCA的离线算法。复杂度为O(n+q)。 这个算法充分利用了dfs树的结构。 对于每个节点u,关于它的询问(u,v)只有两种。(假设先dfs(u)后dfs(v)) 1、v在u的子树内。 此时LCA(u,v) = u. 2、v不在u的子树内。 ⑴假设v在u的父亲的另一棵子树内。 此时LCA(u,v) = father[u]. ⑵如果不满足条件⑴,则v可能在u的父亲的父亲的另一棵子树内...
TarjanLCA
weixin_30700099的博客
10-10 168
LCA问题算是一类比较经典的树上的问题 做法比较多样 比如说暴力啊,倍增啊等等 今天在这里给大家讲一下tarjan算法tarjanLCA是一种稳定高速的算法 时间复杂度能做到预处理O(n + m),查询O(1) 它的主要思想是dfs和并查集 1.输入数据,找出根节点(或输入的)并将存起来 2.输入需要查找的每一对点(两个点),也存起来(也存成) 3.从根节点开始向它的每...
POJ 2874 LCA 树上任意两点距离
java开发指南博客 【转载】
04-21 572
本题说了是无环,所以就是一片森林了。 而对于树上任意两点,我们可以用LCA距离距离为两个子节点到根的距离和减去最近祖先到根的距离的2倍。具体画便可看出来。 并且是无向,所以LCA时需要进行标记 POJ 1986同这道题 基本一样 /* ID: CUGB-wwj PROG: LANG: C++ */ #include &lt;iostream&gt; #include &...
树上两点距离
linli2019的博客
08-02 739
给出一棵树,编号在一段区间内的两两距离
写文章

热门文章

  • gcc编译器 编译的4个步骤 5495
  • 利用Tarjan算法(LCA)求树上任意两点的距离 3524
  • Golang实现简单的Socket网络编程(代码+详细注释) 2573
  • C语言指针与内存 2028
  • 对计算机语言(C语言)的理解 1716

分类专栏

  • C/C++ 14篇
  • Golang 10篇
  • Python 1篇
  • Linux系统编程 10篇
  • Linux网络编程 4篇
  • 数据结构与算法 16篇
  • MySQL 1篇

最新评论

  • 通过C++程序控制MySQL的简单实现

    没伞的男孩: 谢谢哈,一起加油表情包

  • 通过C++程序控制MySQL的简单实现

    出木杉英才: 写得真好!收藏

最新文章

  • 学习Golang的接口
  • 初识Golang的面向对象 为结构体(类)绑定方法
  • 初识Golang的函数
2022年58篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没伞的男孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或 充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家泡沫校园玻璃钢景观雕塑加工玻璃钢布朗熊雕塑定制玻璃钢雕塑福州南充受欢迎的成都商场美陈延安公园玻璃钢雕塑公司商场过道空间美陈苏州玻璃钢商场美陈商场美陈日常布置制度彩虹线商场美陈玻璃钢牛雕塑工厂玻璃钢雕塑销售合同商场美陈场景的设计有没有用玻璃钢做大型雕塑的新上沈阳玻璃钢雕塑加工厂武汉市雕塑玻璃钢工艺厂永州玻璃钢胸像雕塑宣威市玻璃钢雕塑如何廊坊玻璃钢雕塑制作厂家江西环保玻璃钢雕塑供应商甘肃卡通造型玻璃钢雕塑工厂玻璃钢卡通水果雕塑批发汕头玻璃钢卡通雕塑现货泰州儿童玻璃钢雕塑设计辽宁玻璃钢雕塑工程施工标准广东商场主题创意商业美陈多少钱福建商场创意商业美陈怎么样湛江现代玻璃钢卡通雕塑玻璃钢雕塑厂有哪些唐河广场玻璃钢雕塑铜陵户外玻璃钢雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化