最形象的讲解,让你一次学会什么叫LCA离线算法tarjan

Tarjan算法详解(AcWing 1174 受欢迎的牛) 题目描述: 每一头牛的愿望就是变成一头最受欢迎的牛。 现在有 N 头牛,编号从 1 到 N,给你 M 对整数 (A,B),表示牛 A 认为牛 B 受欢迎。 这种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。 你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。 输入格式 第一行两个数 N,M; 接下来 M 行,每行两个数 A,B,意思是 A 认为 B 是受欢迎的(给出的信息有可能重复,即有可能出现多个 A,B)。 输出格式 输出被除自己之外的 阅读详情

今天刚学了LCA离线tarjan算法。下面跟大家分享一下我的学习心得。
首先LCA是Lowest Common Ancestors的缩写。中文名为最近公共祖先。然后就是什么叫离线,与离线相对应的是在线算法(用dfs + rmp)的区别在哪里。
离线算法就是把所有的询问存起来,最后一次解决输出结果。结果有可能需要所有的询问(更新)之后才能得到。
在线算法就是每次询问一次,输出询问结果,每次的结果只与这次的询问有关,与其他询问无关。
我在这里先讲一下离线的tanjan算法,我会陆续的学习在线算法,到时候也会写一篇博客,欢迎大家一起学习,讨论。
tanjan算法是基于并查集实现的,如果还有萌新不知道什么叫并查集,那么请先去学习有关并查集的知识,不然可能会不懂这个tanjan算法。
那么下面我就着重说一下tanjan算法具体是怎样实现的。
首先我们把树上所有的节点的父亲设置成这个节点本身。我们用father[i]表示节点i的父亲,所以这个用代码实现就是

for(int i = 1; i <= n; i++)
{
   father[i] = i;
}

做完之后首先找到整棵树的根节点root,我们从跟节点开始dfs.
dfs的过程是
1.首先判断有没有询问与这个点有关,比如当前dfs到了u这个点,有一个询问是与u有关的,比如其中的一个询问是询问u与点v的最近公共祖先。
那么我们先判断一下v是否已经被访问过,如果被访问过,则u,v的最近公共祖先就是lca = Find(v);其中Find函数实现如下:

int Find(int x)
{
  if(x == father[x]) return father[x];
  else return father[x] = Find(father[x]);
}

如果v没有被访问则直接执行以下步骤:
2.visit[u] = true;
3.dfs u的每一个儿子节点,比如u有一个儿子son,那么先dfs(son),dfs完了之后把son的父亲置为u。(刚开始son的父亲是自己,这个在初始化时已经做完了)
上面所以的实现可以用以下代码表示,其中LCA[i][j] 表示i与j的最近公共祖先。ask[i][j]表示要询问i与j的最近公共祖先,g[i]表示i的儿子向量。

void dfs(int u)
{
   for(int i = 1; i <= n; i++)
   {
      if(visit[i]&&ask[u][i])
      {
        LCA[u][i] = Find(i);
      }
   }
   visit[u] = true;
   for(int i = 0; i < g[u].size(); i++)
   {
     int son = g[u][i];
     dfs(son);
     father[son] = u;
   }
}

可能看了上面的文字讲解和代码实现,你还是有一种似懂非懂的感觉,可能会产生这样一个疑问,为什么这样做是对的?为什么这样做就能求出两个点的最近公共祖先?产生了这样的疑问是好事,说明你在思考,而不是强行记模板。下面我用两张图来简单的说一下为什么这样做就能求出两个点最近公共祖先。
对于任意两个点(u,v),只能有两种情况1.v是u的一个儿子,或者说v在以u为根节点的子树中(或者反过来u是v的儿子,都是一个道理,我们这里为了方便,就假设v是u的儿子(可能不是直接儿子))
2.u,v没有儿子关系

对于第一种情况假如如下图:
这里写图片描述
根据dfs的性质可知,首先dfs到点u,这时发现有一个询问是关于u,v的,但是此时v还没有被访问到,所以在这里先不做处理,继续往下dfs,注意此时虽然u点已经被访问到了,但是father[u]还是u,因为以u为根的整棵子树还没有访问完。然后当dfs到了v时,发现有一个询问是关于v,u的然后判断u有没有被访问,很显然此时u早已经被访问所以LCA[v][u] = Find(u) = u;
对于第二中情况假如如下图:
这里写图片描述
根据dfs的性质,有可能先访问到u,也有可能先访问到v。所以我们可以分为两种情况讨论。
1.先访问到v
当访问到v时,因为u还没有被访问,所以不做处理,当访问完v之后father[v] = x,如图,然后father[x] = f,然后访问到u,这时发现v已经被访问过了,所以LCA(u,v) = Find(v) = Find(x) = Find(f) = f.

2.先访问到u.
当访问到u时,因为v还没有被访问,所以不做处理,当访问完以u为根的子树后,father[u] = f;然后访问x,然后访问v.当访问到v时,发现u已经被访问了,所以LCA(v,u) = Find(u) = f;

至此,上面两种情况已经讨论完了,你现在应该已经明白了为什么这样做是对的的原因。

那么理论知识我们学习完了,我们下面去实践一下,我们以poj1470为例。
ac代码如下:

#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 1000;
int ask[maxn][maxn];//保存询问
int ans[maxn];//保存祖先i出现过的次数
int n,m;
vector<int> g[maxn];//保存儿子
int root;//树的根
bool visit[maxn];
bool isroot[maxn];
int father[maxn];
int Find(int x)
{
    if(father[x] == x) return x;
    else return father[x] = Find(father[x]);
}
void init()
{
    memset(ans,0,sizeof(ans));
    memset(visit,false,sizeof(visit));
    memset(isroot,true,sizeof(isroot));
    memset(ask,0,sizeof(ask));
    for(int i = 1; i <= n; i++)
    {
        g[i].clear();
        father[i] = i;
    }

}
void LCA(int root)
{
    for(int i = 1; i <= n; i++)
    {
        if(visit[i]&&ask[root][i])
        {
            ans[Find(i)] += ask[root][i];
        }
    }
    visit[root] = true;
    for(int i = 0; i < g[root].size(); i++)
    {
        int term = g[root][i];
        LCA(term);
        father[term] = root;
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        int f,s,num;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d:(%d)",&f,&num);
            for(int j = 1; j <= num; j++)
            {
                scanf(" %d",&s);
                isroot[s] = false;
                g[f].push_back(s);
            }
        }
        for(int i = 1; i <= n; i++)
        {
            if(isroot[i])
            {
                root = i;
                break;
            }
        }
        scanf("%d",&m);
        int u,v;
        for(int i = 1; i <= m; i++)
        {
            scanf(" (%d %d)",&u,&v);
            ask[u][v]++;
            ask[v][u]++;
        }
        LCA(root);
        for(int i = 1; i <= n; i++)
        {
            if(ans[i])
            {
                printf("%d:%d\n",i,ans[i]);
            }
        }
    }
    return 0;
}

以上就是我今天学了一上午的LCA离线tarjan算法的心得,希望对大家有用,如果上面有什么不对的地方,欢迎各位大佬指出.

Tarjan算法详解 目录一、什么是强连通分量1. 概念2. 强连通分量二、两种dfs遍历1. 方式12. 方式2三、一个简单例子理解算法四、更完整的一个例子五、Code实现 一、什么是强连通分量 tarjan强连通分量算法 1. 概念 连通:无向图中,从任意点i可到达任一点j 强连通:有向图中,从任意点i可到达任一点j 弱连通:把有向图看作无向图时,从任意点i可到达任一点j 如图,强连通无论那个点,都能按照方向到达任意一点,弱连通如果强行按方向,那么B到不了C,A到不了B和C,C到不了B。但如果把他看作是无向图,那么他们也 阅读详情

相关推荐

算法学习笔记(Tarjan)

本文介绍Tarjan求强联通分量、找割点和割边、找环。

qq_74405082的博客 2180

在线算法和离线算法的概念(转)

一、在线算法   在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。相对的,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。例如,选择排序在排序前就需要知道所有待排序元素,然而插入排序就不必。   因为在线算法并不知道整个的输入,所以它被迫做出的选择最后可能会被证明不是最优的,对在线算...

lang_a的博客 8340

Klocwork安装

简单介绍一下Klocwork在windows下的安装,操作系统是win7,Klocwork的版本是10.0。 第一步:由于Klocwork的安装程序已十分成熟,所以在安装之前不需要什么准备共走。双击Klocwork的安装包,会出现下图的安装引导界面,点击Next按钮。 第二步:下图是协议界面,点击Next按钮。 第三步:选择Klocwork的安装目录,点击Next按钮 第四步:

旮旯 9931

浅谈 Tarjan 算法

无向图中,若删去一条边会使得这个图的极大连通分量数增加,则该边被称为桥。也可以理解为无向图的一个连通块中,若删除一条边会使得至少两点之间无法相互到达,该边被称为桥。无向图中,若删去一个点会使得这个图的极大连通分量数增加,这个点被称为割点。也可以理解为无向图的一个连通块中,若删除一个点会使得至少两点之间无法相互到达,该点被称为割点。注意,强连通分量(SCC,Strongly Connected Components)是在单向图中的。强联通子图,定义为:在单向图中,该子图上的任意两点之间能互相到达。

William_Wang_的博客 5220

使用QGIS配准无人机航片

随着无人机在各个行业的普及,无人机航片成为很多应用系统空间数据的重要来源。无人机航片具有成像快、分辨率高、针对性强的优点,广泛用于保险定损、灾情应急监测、设施巡检、农作物面积调查等方面。 然而,由于种种原因,通过无人机拍摄的照片拼接得到的正射航片与其他地理空间数据叠加时经常出现较明显的偏差,需要进行配准以校正其空间位置,因此,本文将使用QGIS的配准工具演示无人机正射航片配准的过程。 01数据情况 本文选用的示范数据为2020年7月中左右无人机拍摄的河北省某地系列照片,经过拼接得到CG...

QGISClass的博客 5249

电赛MSP430F5529之超声波测距+OLED 显示(含主要代码)

目录 前言 一、超声波介绍 二、部分代码 三、总结 题外话: 前言 哎,又双叒叕,电赛延期了,看起来像是遥遥无期,但是肯定要考虑大局,大家就继续抓紧时间学习知识,巩固自己的知识,更进一步。好了,今天主要是继承上一篇博文(OLED显示)写的,主要是写超声波测距的(US-015) 一、超声波介绍 利用TI公司的MSP430F5529单片机+超声波模块+OLED 实现 这里主要是利用单片机定时器的捕获功能来捕获超声波高电平持续时间,再计算得到距离,最后再在OLED 屏幕上显示出来,上图

不说二话的自家人的博客 1万+

Tarjan算法超超超详解(ACM/OI)(强连通分量/缩点)(图论)(C++)

深入浅出的Tarjan算法手把手讲解!看完就会!

qaqwqaqwq的博客 1万+

手把手教你用Boot-Repair快速修复Ubuntu启动分区缺失(附详细命令行步骤)

本文详细介绍了如何使用Boot-Repair工具快速修复Ubuntu启动分区缺失问题,包括诊断、应急准备、自动修复和手动命令操作。通过grub rescue模式下的关键命令和Boot-Repair的图形界面操作,帮助用户高效解决启动分区故障,确保系统恢复正常运行。

weixin_29229989的博客 563

Tarjan 算法学习笔记

支持度(Support)是指数据集DDD中包含某个项集XXX的记录所占的比例。SuppXfreqX∣D∣SuppX∣D∣freqX​其中freqXfreq(X)freqX表示包含XXX的记录数,∣D∣|D|∣D∣表示数据集DDD中记录的总数。置信度(Confidence)是指数据集DDD中包含项集XXX的记录中同时包含另一个项集YYY的比例。

m0_72410588的博客 1699

正版63棋牌源代码游戏+728游戏全套带前端源代码解析

这俩天抽时间仔细看了一下这套项目包含完整的前端、后端及后台管理系统。从上传的截图可以看出,前端基于资源目录,后端采用和,后台管理界面提供数据查询与管理功能。本篇文章将从前端结构、后端接口、数据库设计、常见 BUG 及优化方法等方面逐步拆解,并给出相应的代码示例和实践经验。

aa2031171的博客 3581

全面分析总结解决Could not transfer artifact org.apache.maven.plugins等此类问题的方法

全面分析总结解决Could not transfer artifact org.apache.maven.plugins,Could not transfer artifact xxx from/to xxx等此类问题的方法

ccjjjjdff的博客 1万+

LCA

作为一个数据结构学傻,终于要开始入这个坑了…… 之前一直不写有关数据结构的东西,是因为不会画那种很高端的图,所以一直不写,现在看来似乎没有办法避免了…… LCA,全称Least Common Ancestor,即最近公共祖先,顾名思义,LCA(u,v)就是u和v的所有共同祖先中深度最小的,很多算法对于树上一根链的处理通常都需要围绕LCA展开,因此快速求得树上两个节点的LCA是树上算法的基...

as7223979的博客 286

跨应用服务器的自定义 IDoc 传输实战:以 CATSDB 工时为例,打通两套 SAP 系统的数据通道

本文介绍了在企业多SAP系统环境下,使用SAP ALE/IDoc技术实现工时数据跨系统传输的解决方案。文章从技术选型、系统架构到具体配置步骤进行了详细说明,重点阐述了IDoc/ALE在异步传输、全链路追踪和数据重放方面的优势。 文章详细讲解了端到端实施流程,包括Logical System配置、RFC Destination创建、Message Type定义、Segment和Basic Type设计等关键步骤。同时提供了出站封装的ABAP代码示例,强调调用MASTER_IDOC_DISTRIBUTE函数后执

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 173

关于LCA的三种解法

百度百科关于LCA的解释:LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。(有多种变型例如求两点间的距离如HDU2586,求最大公共的长度如CodeForces - 832D等等) 题目:POJ 1984 HDU 2586ZOJ 3195POJ 1330CodeForces - 832D 1.跳跃...

weixin_30649859的博客 126

GEE必须会教程——一文教会你研究区域分块计算与合并

一整块算不是挺好的吗?emmmm,用过的人都知道,GEE好比幼年期的“紫妍”,要用“丹药”来养的,除非你是萧炎小友,到哪都有人给爆金币。我们今天想做的是计算2020年夏季西藏自治区的植被覆盖度(FVC),FVC的估算需要使用归一化植被指数(NDVI数据),这里我们用到的产品是MODIS的相关数据产品,该产品的分辨率是25 0m。下载好的结果毕竟是一块一块的,怎么对下载好的结果进行拼接,变成一张大图呢,别急,接下来,我们上科研小霸王——R语言。此处,我们通过省份数据,按照属性筛选,得到西藏自治区的行政边界。

Promising_GEO的博客 2512

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

强连通分量-tarjan算法缩点

强连通分量:在有向图G中,如果两个顶点u,v间(u->v)有一条从u到v的有向路径,同时还有一条从v到u的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。简单点说就是:如果一个有向图中,存在一条回路,所有的结点至少被经过一次,这样的图为强连通图。在强连图图的基础上加入一些点和路径,使得当前的图不在强连通,称原来的强连通的部分为强连通分量。

c__chong的博客 3501

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Tarjan 算法(超详细!!)

之前太多错误了,重构了

WerChange的OI博客 7288

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Tarjan算法(详解 + 刷题)

包含 4 道 Tarjan 图论题目和AC代码(讲解)

IT OR 考公? 3344

tarjan算法(相关概念、Tarjan求最大强连通分量、割点、求桥、缩点)

无向图 G 的最大连通子图称为 G 的连通分量。注:这里的最大连通子图的含义为:此图为 G 的连通子图,将 G 的任意一个点加到盖子图中之后,此子图将不再连通。比如这一张图中,很显然,我用三种颜色圈起来的部分都是原图的连通分量。(其实可以十分形象的理解成每一个连通分量都是一个连通块)回想到以前的“亲戚”一题,就是让我们去求在这个无向图中,询问的两个点是不是在同一个连通分量中。值得注意的一点,一个连通图的连通分量就是他本身。

Jiguoyuanyic的博客 1905

tarjan—算法的神(一)

本篇包含 tarjan 求强连通分量、边双连通分量、割点 部分, tarjan 求点双连通分量、桥(割边)在下一篇。 伟大的 Robert Tarjan 创造了众多被人们所熟知的算法及数据结构,最著名的如:(本文的)连通性相关的 tarjan 算法,Splay-Tree,Toptree,tarjan 求 lca 等等。 注:有向图的强连通分量、无向图的双连通分量、tarjan 求最近公共祖先...

weixin_40393403的博客 926

tarjan 算法(Tarjan‘s strongly connected components algorithm)

我们可以想象有向图中边一直往前冲,突然有一个边是往回走的,这样就会连接到刚才已经遍历过的点(可能形成一个环),但是这种边我们需要判断是否连接到了另一个SCC,所以我们需要记录点是否在栈内,如果在栈内说明两个点属于同一个SCC,所以可以更新,如果不在,就不能更新当前点的。当 DFS 遍历过程中发现一个节点的所有后继节点已经搜索完毕,并且该节点是当前 DFS 搜索树中的根节点时,可以将该节点以及其所有后继节点弹出栈,并将它们标记为一个强连通分量。同时我们需要知道该点是否在栈内,定义一个记录数组。

qq_44823758的博客 1096

图的连通性问题之tarjan算法

Tarjan算法一种由Robert Tarjan提出的求解有向图强连通分量的线性时间的算法。 Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。 定义dfn(u)为节点u搜索的次序编号,low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。 当dfn(u)

cax1165 2811

关于tarjan的浅解

这个tarjan主要是为了解决一些关于图的问题。

qq_64468032的博客 542

Tarjan算法缩点

在DFS的过程中,当一个节点的所有邻接节点都被访问完毕后,该节点才会被标记为一个强联通分量的结束点。这是因为Tarjan算法在DFS遍历过程中,最后一个被标记的强联通分量在缩点图中没有入边,从而在拓扑排序中应该是第一个节点。由于Tarjan算法是深度优先的,最后一个被标记的强联通分量在缩点图中没有入边,这意味着它在拓扑排序中应该是第一个节点。在有向无环图中,由于没有环的存在,我们可以利用拓扑排序来确定节点的处理顺序,然后使用动态规划来计算最短路径。你只需要求出这个权值和。共一行,最大的点权之和。

qq_73702550的博客 2245

Tarjan 算法学习笔记(超详细)

对于一张有向图\(S\)的子图\(V\),满足\(V\)内的点两两之间可达,并且不存在一个子图\(G\)使得,通俗的讲就是无法继续扩张的两两可达的子图。对于无向图(注意:图不一定连通),割边的定义为:去掉这条边后图的连通块数量增加。对于无向图(注意:图不一定连通),割点的定义为:去掉这个点后图的连通块数量增加。

weixin_41496326的博客 170
上一篇: codeforces/contest/798/problem/B
下一篇: Seven Segment Display(数位dp)
creatorx
博客等级 码龄10年 127粉丝 145原创
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值