Tarjan算法

这篇具有很好参考价值的文章主要介绍了Tarjan算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Tarjan算法

1 算法简介

还记得无向图判连通块吗?对于无向图中,判连通块是一件很容易的事。你只需要dfs(深度优先搜索)一下就可以了。但是,如果我们把无向图换成有向图呢?

这就是另一个故事了…

2 算法定义

Robert Tarjan计算机科学家,以LCA,Tarjan等算法闻名。Tarjan是求强连通分量的一个强力的算法。

要理解Tarjan这个算法,我们先讲几个定义:

强连通分量 :

对于一个分量,若任意两个点相通,则称为强连通分量。

树边 :

对于一个图的dfs树,它的树边便是此图的树边。

返祖边 :

对于一个图的dfs树,可以使得儿子节点返回到它的祖先的边为返祖边。

横插边 :

对于一个图的dfs树,可以使得一个节点到达另一个节点且它们互不是祖先的边为横插边。

神奇海螺结束!

tarjan算法,图论,# Tarjan,算法,图论,c++

3 算法详细

先讲一下我们要用到的数组。

  • dfn:第 i i i个节点他的时间戳

  • low:第 i i i个节点他最多经过一条返祖边所能到达的最小时间戳

  • st:一个栈,用来储存当前还未确定但已经扩展过的点

  • co:第 i i i个节点他所在的强连通分量编号

我们讲一下算法流程。

  1. 此时来到了点 u u u

  2. 扩展他的子节点,这里假设现在到的子节点为 v v v,扩展完了就来到第 5 5 5

  3. 三种情况:

    • !dfn[v],即还未扩展的点,则我们直接来到第 4 4 4
    • !co[v]&& dfn[v],即还未出栈但已扩展过的点(就是返祖/横叉了嘛),我们更新一下 l o w u = min ⁡ ( l o w u , d f n v ) low_u = \min(low_u, dfn_v) lowu=min(lowu,dfnv),并返回第 2 2 2步。(看不懂的感性理解一下)
    • co[v] && dfn[v],即已出栈且遍历过,那么我们直接返回第 2 2 2
  4. 我们先对 v v v这个顶点扩展一下(即返回第 1 1 1步),然后把 l o w u = min ⁡ ( l o w u , l o w v ) low_u = \min(low_u, low_v) lowu=min(lowu,lowv)更新一下,接着回到第 2 2 2

  5. 如果 d f n u = l o w u dfn_u = low_u dfnu=lowu,这说明 u u u没有返祖边,它拥有属于自己的一棵子树,而此时栈中的点又一定能到 u u u(要不然就被弹掉了),所以此时我们只需要弹栈就可以了!

  6. 我们已经完成了所有操作,可以回溯到第 1 1 1步了

4 模板代码

先放一道模板题 : CF427C Checkposts

这题是一道赤裸裸的强连通分量,晾一下代码。文章来源地址https://www.toymoban.com/news/detail-623939.html

# include <bits/stdc++.h>
using namespace std;

# define ll long long
# define lf double
# define GO(i,a,b) for(int i = a; i <= b; i ++)
# define RO(i,b,a) for(int i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a,b)
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

CI maxn = 1e5 + 7;
CI maxm = 3e5 + 7;
CI mod = 1e9 + 7;

int n, m;

struct Edge{
	int u, v, next;
}e[maxm];

int head[maxn], cnt;

void add(int u, int v){
	e[++ cnt].u = u;
	e[cnt].v = v;
	e[cnt].next = head[u];
	head[u] = cnt;
}

int dfn[maxn];
int low[maxn];
int vis[maxn]; // 0未访问, 1在栈中, 2已访问 
int tar[maxn]; // 连通分量 
int col;

int tmp;

stack <int> q;

void Tarjan(int u){
	q.push(u);
	vis[u] = 1; // 在栈中 
	low[u] = dfn[u] = ++ tmp;
	FO (i, u, head, e){
		int v = e[i].v;
		if (vis[v] == 2)
			continue;
		if (dfn[v])
			low[u] = min <int> (low[u], dfn[v]);
		else{
			Tarjan(v);
			low[u] = min <int> (low[u], low[v]);
		}
	}
	if (dfn[u] == low[u]){
		int v = q.top();
		q.pop();
		col ++;
		while (!q.empty() && v != u){
			vis[v] = 2;
			tar[v] = col;
			v = q.top();
			q.pop();
			
		}
		vis[u] = 2;
		tar[u] = col;
	}
}

vector <int> poi[maxn];

ll a[maxn];

int main(){
	cin >> n;
	GO (i, 1, n)
		scanf("%lld", &a[i]);
	cin >> m;
	GO (i, 1, m){
		int u, v;
		scanf("%d %d", &u, &v);
		add(u, v);
	}
	
	GO (i, 1, n)
		if (!vis[i])
			Tarjan(i);
	
	GO (i, 1, n)
		poi[tar[i]].PB(i);
	
	ll ans1 = 1, ans2 = 0;
	GO (i, 1, col){
		ll minn = 2e18;
		ll res = 0;
		for (int j : poi[i])
			minn = min <ll> (minn, a[j]);
		for (int j : poi[i])
			if (a[j] == minn)
				res ++;
		ans2 += minn;
		ans1 = (ans1 * res) % mod;
	} 
	
	printf("%lld %lld", ans2, ans1);
	return 0;
}

到了这里,关于Tarjan算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Tarjan算法

    1 算法简介 还记得 无向图判连通块 吗?对于无向图中,判连通块是一件很容易的事。你只需要 dfs(深度优先搜索) 一下就可以了。但是,如果我们把无向图换成 有向图 呢? 这就是另一个故事了… 2 算法定义 Robert Tarjan 计算机科学家,以 LCA , Tarjan 等算法闻名。 Tarjan 是求强连

    2024年02月14日
    浏览(38)
  • Tarjan 算法(超详细!!)

    推荐在 cnblogs 上阅读 说来惭愧,这个模板仅是绿的算法至今我才学会。 我还记得去年 CSP2023 坐大巴路上拿着书背 Tarjan 的模板。虽然那年没有考连通分量类似的题目。 现在做题遇到了 Tarjan,那么,重学,开写! 另,要想学好此算法的第一件事——膜拜 Tarjan 爷爷。 其实广义

    2024年01月25日
    浏览(44)
  • 浅谈 Tarjan 算法

    在了解 Tarjan 算法之前,我们先来了解 dfs 搜索树。 定义: dfs 遍历整张图,按照 dfs 序构成一棵树。 1.1 有向图的 dfs 生成树 有向图的 dfs 生成树包括四种边: 树边(tree edge):图中黑色边表示。表示搜索访问到未访问过的结点。 回边(back edge):图中橙色边表示。表示该边

    2024年02月05日
    浏览(40)
  • 20 求图的割点和割边—Tarjan算法

    问题描述 去掉2号城市,这样剩下的城市之间就不能两两相互到达。例如4号城市不能到5号城市,6号城市也不能到达1号城市等等。 下面将问题抽象化。在一个无向连通图中,如果删除某个顶点后,图不再连通(即任意两点之间不能相互到达),我们称这样的顶点为割点(或者

    2024年02月15日
    浏览(34)
  • 「学习笔记」tarjan求最近公共祖先

    Tarjan 算法是一种 离线算法 ,需要使用并查集记录某个结点的祖先结点。 并没有传说中的那么快。 将询问都记录下来,将它们建成正向边和反向边。 在 dfs 的过程中,给走过的节点打上标记,同时维护并查集,这里利用了回溯的思想,如果 (u) 节点的这棵子树没搜完,那么

    2024年02月01日
    浏览(41)
  • 「学习笔记」tarjan 求最近公共祖先

    Tarjan 算法是一种 离线算法 ,需要使用并查集记录某个结点的祖先结点。 并没有传说中的那么快。 将询问都记录下来,将它们建成正向边和反向边。 在 dfs 的过程中,给走过的节点打上标记,同时维护并查集,这里利用了回溯的思想,如果 (u) 节点的这棵子树没搜完,那么

    2024年02月01日
    浏览(50)
  • AcWing1171. 距离(lca&&tarjan)

    输入样例1: 输出样例1: 输入样例2: 输出样例2:

    2024年02月14日
    浏览(35)
  • LeetCode 周赛 341 场,模拟 / 树上差分 / Tarjan 离线 LCA / DFS

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 上周末有单双周赛,双周赛我们讲过了,单周赛那天早上有事没参加,后面做了虚拟竞赛,然后整个人就不好了。前 3 题非常简单,但第 4 题有点东西啊,差点就放弃了。最后,

    2023年04月20日
    浏览(42)
  • Codeforces Round 916 (Div. 3)(E:贪心 F贪心dfs G tarjan+topsort +线段树优化建图)

    A:直接暴力统计每个字符的次数是否达标即可 B:直接先输出所需要的k,然后降序输出剩下的即可 C: 直接枚举最后操作到哪个位置就行了,然后贪心一直操作1到i的位置的b最大值即可 D: 先固定第一次是A 第二次是B 第三次是C 枚举B为中介点i,然后求1到i-1的A的最大值,和

    2024年01月17日
    浏览(41)
  • 【C++算法竞赛 · 图论】图论基础

    前言 图论基础 图的相关概念 图的定义 图的分类 按数量分类: 按边的类型分类: 边权 简单图 度 路径 连通 无向图 有向图 图的存储 方法概述 代码 复杂度 图论(Graph theory) ,是 OI 中的一样很大的一个模块,围绕它有很多高难度的算法以及高级的概念。 这篇文章将介绍关

    2024年04月12日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包