首页 > 其他分享 >[学习笔记]强连通分量

[学习笔记]强连通分量

时间:2023-10-14 22:44:07浏览次数:36  
标签:连通 tot int 笔记 ++ dfn low col 分量

定义

什么是强连通分量?直白地说就是在一个有向图中,有一块区域,每个点都可以互相抵达。这里用一张图来说明一下。

图中的 \(1, 2, 3\) 是一个强连通分量,因为他们可以互相抵达。

Tarjan 算法

如何求强连通分量,最有名且最常用的就是 Tarjan 算法。

先给出如下定义:

  • \(dfn_u\):深搜时被第几个遍历,称为时间戳。
  • \(low_u\):在 \(u\) 的子树中能回溯到的最早在栈中的节点。

分 3 种情况考虑:

  • 当 \(v\) 未被访问,那么就继续访问,同时更新 \(low_u\) 。
  • 当 \(v\) 被访问过,且在栈当中,用 \(dfn_v\) 更新 \(low_u\) 。
  • 当 \(v\) 被访问过,且不在栈中,说明已经处理完毕,不用继续处理。

同时,如果在回溯中 \(dfn_u = low_u\) ,说明栈中 \(u\) 和它上方的节点构成强连通分量。

接下来给出模板代码

struct Edge{
	int v, nxt;
}edge[N];
int head[N], dfn[N], low[N], col[N], v[N], sum[N];
//                           强连通分量编号,点权,强连通分量点权
void Tarjan(int u)
{
	dfn[u] = low[u] = ++indx; st.push(u);
	for (int i = head[u]; i; i = edge[i].nxt)
	{
		int v = edge[i].v;
		if (!dfn[v]){Tarjan(v); low[u] = min(low[u], low[v]);}
		else if (!col[v])low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u])
	{
		tot++;
		int x;
		do{
			x = st.top(); st.pop(); col[x] = tot; sum[tot] += v[x];
		}while (x != u);
	}
}

接下来看几道例题。

例题

P3387 【模板】缩点

强连通分量 + 缩点,先求强连通分量,再进行缩点的过程,最后输出答案。

Accepted Code

/*上古时期写的,不知链式前向星*/
#include <bits/stdc++.h>
using namespace std;
const int N = 114514;
vector<int> edge[N];
vector<int> newG[N];
int f[N], dfn[N], low[N], col[N], W[N], sum[N], ru[N], st[N], que[N], ans;
int inde, tot, top, qr;
void add_edge(int u, int v){edge[u].push_back(v);}
void tarjan(int u)
{
	dfn[u] = low[u] = ++inde;
	st[++top] = u;
	for (int i = 0; i < edge[u].size(); i++)
	{
		int v = edge[u][i];
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (!col[v])low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u])
	{
		col[u] = ++tot;
		sum[tot] += W[u];
		int x;
		while (x = st[top--], x != u)col[x] = tot, sum[tot] += W[x];
	}
}
int main()
{
	int n, m, i, j, u, v, x;
	cin >> n >> m;
	for (i = 1; i <= n; i++)cin >> W[i];
	for (i = 1; i <= m; i++)
	{
		cin >> u >> v;
		add_edge(u, v);
	}
	for (i = 1; i <= n; i++)if (!dfn[i])tarjan(i);
	for (i = 1; i <= n; i++)
		for (j = 0; j < edge[i].size(); j++)
		{
			x = edge[i][j];
			if (col[i] != col[x])newG[col[i]].push_back(col[x]), ru[col[x]]++;
		}
	for (i = 1; i <= tot; i++)
		if (!ru[i])que[++qr] = i, f[i] = sum[i];
	for (i = 1; i <= qr; i++)
	{
		u = que[i];
		for (int j = 0; j < newG[u].size(); j++)
		{
			v = newG[u][j];
			f[v] = max(f[v], f[u] + sum[v]);
			if (!--ru[v])que[++qr] = v;
		}
	}
	for (i = 1; i <= tot; i++)ans = max(ans, f[i]);
	cout << ans;
	return 0;
}

P3627 [APIO2009] 抢掠计划

某次校内模拟赛原题,当时竟然没想到是强连通分量。

像上一题那样,先求强连通分量,再缩点,然后用拓扑排序/某个已死的算法SPFA,求出一条最长路,答案为 \(\max\limits_{1 \le i \le p}\{dis_{col_{t_i}}\}\)

Accepted Code

/*Code By Manipula*/
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5 + 5;
struct Edge{
	int v, nxt;
}edge1[N], edge2[N];
stack<int> st;
int head1[N], head2[N], dfn[N], low[N], rn[N], col[N], v[N], t[N], sum[N], dis[N];
int indx, num1, num2, tot, n, m, s, p, ans;
void add1(int u, int v){edge1[++num1] = (Edge){v, head1[u]}; head1[u] = num1;}
void add2(int u, int v){edge2[++num2] = (Edge){v, head2[u]}; head2[u] = num2;}
int find(int u, int v)
{
	for (int i = head2[u]; i; i = edge2[i].nxt)
		if (v == edge2[i].v)return 1;
	return 0;
}
void Tarjan(int u)
{
	dfn[u] = low[u] = ++indx; st.push(u);
	for (int i = head1[u]; i; i = edge1[i].nxt)
	{
		int v = edge1[i].v;
		if (!dfn[v]){Tarjan(v); low[u] = min(low[u], low[v]);}
		else if (!col[v])low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u])
	{
		tot++;
		int x;
		do{
			x = st.top(); st.pop(); col[x] = tot; sum[tot] += v[x];
		}while (x != u);
	}
}
void toposort()
{
	queue<int> q;
	memset(dis, -0x3f3f3f3f, sizeof(dis));
	for (int i = 1; i <= tot; i++)if (!rn[i])q.push(i);
	dis[col[s]] = sum[col[s]];
	while (!q.empty())
	{
		int u = q.front(); q.pop();
		for (int i = head2[u]; i; i = edge2[i].nxt)
		{
			int v = edge2[i].v;
			dis[v] = max(dis[v], dis[u] + sum[v]);
			if (--rn[v] == 0)q.push(v);
		}
	}
}
signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int u, v; cin >> u >> v;
		add1(u, v);
	}
	for (int i = 1; i <= n; i++)cin >> v[i];
	cin >> s >> p;
	for (int i = 1; i <= p; i++)cin >> t[i];
	for (int i = 1; i <= n; i++)if (!dfn[i])Tarjan(i);
	for (int i = 1; i <= n; i++)
		for (int j = head1[i]; j; j = edge1[j].nxt)
		{
			int v = edge1[j].v;
			if (col[i] != col[v] && !find(col[i], col[v]))
				add2(col[i], col[v]), rn[col[v]]++;
		}
	toposort();
	for (int i = 1; i <= p; i++)ans = max(ans, dis[col[t[i]]]);
	cout << ans;
	return 0;
}

习题

暂无。

标签:连通,tot,int,笔记,++,dfn,low,col,分量
From: https://www.cnblogs.com/Manipula/p/17764909.html

相关文章

  • 学习笔记五
    第11章EXT2文件系统EXT2文件系统数据结构:EXT2文件系统使用一些关键的数据结构来组织文件和目录的存储和访问。以下是EXT2文件系统中常见的数据结构:超级块(Superblock):是文件系统的起始部分,包含关键的元数据,如文件系统的大小、块的数量、inode(索引节点)的数量等信息。块组描......
  • 2023_10_14_MYSQL_DAY_05_笔记
    2023_10_14_MYSQL_DAY_05_笔记https://www.cnblogs.com/tdskee/p/16536166.html{MySQL的优化多种方法(至少15条)}#查看触发器showtriggers;#删除触发器droptrigger触发器名;#建立触发器droptriggerifexistsdept_del;createtriggerdept_delafterdeleteon......
  • 《敏捷软件开发宣言》阅读笔记二
    敏捷软件开发宣言的核心内容敏捷软件开发的原则《敏捷软件开发宣言》提出了四个基本原则:简洁、沟通、反馈和适应。这些原则构成了敏捷软件开发的基础,帮助团队在面对变化和不确定性时,能够迅速做出调整。敏捷软件开发的价值观敏捷软件开发宣言提出了12个价值观,包括:个体和互......
  • 网络安全笔记DAY01
    实战:MS17-010(永恒之蓝)漏洞测试实战背景:2017年4月14日晚,黑客团体ShadowBrokers(影子经纪人)公布一大批网络攻工具,其中包含“永恒之蓝”工具,“永恒之蓝”利用Windows系统的SMB漏洞可以获取系统最高权限。恶意代码会扫描开放445文件共享端口的Windows机器,无需用户任何操作,只要开机上网......
  • 学习笔记5(第十一章)
    一、知识点归纳(一)知识点整理第十一章EXT2文件系统EXT2是一个完全与LINUX兼容的文件系统,这一章在简要EXT2-EXT4的当前状况之后,又用编程示例各种数据结构与如何进行相关的实现还展示了如何通过虚拟磁盘mount-root来构建基本文件系统,将文件系统的实现分为了三个级别并分别介绍。......
  • 20211325 2023-2024-1 《信息安全系统设计与实现(上)》第五周学习笔记
    202113252023-2024-1《信息安全系统设计与实现(上)》第五周学习笔记一、任务要求自学教材第11章,提交学习笔记(10分),评分标准如下:1.知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)“我在学***X知......
  • openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文
    openGauss学习笔记-99openGauss数据库管理-管理数据库安全-客户端接入认证之配置文件参考99.1参数说明表1参数说明参数名称描述取值范围local表示这条记录只接受通过Unix域套接字进行的连接。没有这种类型的记录,就不允许Unix域套接字的连接。只有在从服务器本机......
  • 【科研00】【论文阅读】【略读笔记】TransUnet
    目录0.引言1.链接Link2.阅读Read2.1.结构Structure2.2.编码Encoder2.2.1.卷积CNN2.2.2.变换Transformer2.3.解码Decoder3.优势Advantage4.想法Think0.引言  想尝试TransUnet,先稍微的了解了一下结构。  如果阅读到这篇文章,请略过,本文仅是个人的随笔。......
  • [14/10/23] 微积分学习笔记(查漏补缺ver)
    水个博客。。。好久没上了xxx下面是正文--微积分学习过程中的乱七八糟的数学手册1.致密性定理:任何有界数列必定有收敛的子列。证明思路:由于对于一个任意给定的有界数列\(\{a_n\}\),有唯一数列\(\{b_n\}=\{-a_n\}\)与之对应,则很容易想到只需证明存在单增(或单减,因为......
  • 十一章学习笔记
    章节概述本章内容为EXT2文件系统,作为Linux系统最传统的磁盘文件系统,EXT2文件系统是理解Linux下文件系统的关键。本章介绍了EXT2在Linux系统中的历史地位,以及其后的EXT3、EXT4文件系统的当前应用状况;展示了EXT2文件系统的数据结构以及对EXT2文件树系统的遍历;介绍了如何......