首页 > 其他分享 >Tarjan 之 SCC 与 缩点

Tarjan 之 SCC 与 缩点

时间:2024-08-19 21:27:05浏览次数:11  
标签:缩点 Tarjan 遍历 int SCC dfn low 节点

这篇文章将讲述作者对 Tarjan求SCC与缩点(不是割点)的理解

让我们开始吧!

Tarjan SCC 与 缩点

既然要求 \(SCC\) 那我们先要弄明白 什么是 SCC

SCC 指的是强连通分量

强连通指的是若一张有向图的节点两两互相可达,则这张图是强连通的

强连通分量 指的是一个极大连通子图
此处的极大指的是一个子图再多一个节点都将不强连通

那么知道了这个,我们就可以继续了

tarjan求SCC的算法和之前求 LCA 的Tarjan 有些类似之处

这是tarjan求SCC所需的数组

  • \(dfn[x]\) 表示 \(x\) 节点的DFN序即遍历顺序
  • \(low[x]\) 表示 \(x\) 节点所能到达的 DFN序最小 的节点 PS:可以经过多条边
  • \(stk[]\) 求SCC所需的栈
  • \(instk[x]\) 表示 \(x\) 节点是否在栈中
  • \(scc[x]\) 表示 \(x\) 节点在编号为几的 SCC之中
  • \(siz[k]\) 表示 编号为 \(k\) 的一个SCC的大小

其中 \(dfn\) , \(low\) , \(instk\) 和 \(scc\) 都是可以放在一个结构体中的

数组有点多,不要慌, 我们一点点来讲讲算法的流程

首先,对于一个图,我们在深搜遍历时,每个点只会进入一次,而这将产生一个搜索树

例如对这个图
image

我们会产生一颗如下的搜索树
image

当我们在深搜进入点 \(x\) 时, 标记其 的深搜序 然后把它放入栈中

然后遍历他的所有边能到的点 \(y\)

y 有且仅有 3 种情况

  1. y没被遍历过且不在栈中 对于这种情况,我们继续递归 \(y\) 然后 用 \(low[y]\) 来更新 \(low[x]\) 就是将 \(low[x]\) 变成 \(low[x]\)与\(low[y]\) 最小的那个

  2. y被遍历过且在栈中,对于这种情况我们用 \(low[y]\) 来更新 \(low[x]\)

  3. y被遍历过且在栈中,说明这个点我们已经处理完了,就不用管了

在遍历完后,我们检查 \(dfn[x]\) 是否等于 \(low[x]\)
若相等,则说明 \(x\) 是一个 SCC 的根

先证明为什么,然后再继续讲

由 \(low[x]\) 的定义可得,\(x\) 所能到达的 \(dfn\) 编号最小的节点就是 他自己

就像这个图

image

由于 \(x\) 能够走到他自己,那么 \(x\) 一定在一个环中,再感性理解一下(我还不知道怎么证明这是极大的 QwQ upt:证明见下面),\(x\) 一定是一个 SCC 的根

所以,我们不断弹栈知道将 \(x\) 弹出去,将弹出去的每个节点标记所在的 SCC 编号,然后取消入栈标记, 将 \(siz[编号]++\)

这是因为以 \(x\) 为根的 SCC 一定都在 \(x\) 之后入栈 ,而所有的不在 以 \(x\) 为根的 SCC 中 的 节点 一定 在回溯到 \(x\) 之前已经被相同的流程处理掉了,已经出栈了,栈中还在 \(x\) 之后的一定都属于以 \(x\) 为根的 SCC

以一个图为例子
image

还有没讲详细的后面再补吧,结合代码感性理解一下

上代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct node
{
	vector<int> to;//能到达的点
	int dfn;
	int low;//能访问到的dfn最小的节点
	bool instk;
	bool vis;
};
node nodes[1000000];
int tot;//dfn
int stk[100000],scc[100000],siz[100000]/*SCC大小*/,cnt/*SCC编号*/,top/*栈顶*/;

void tarjan(int x)
{
	nodes[x].dfn=++tot;//记录DFN
	nodes[x].low=tot;
	stk[++top]=x;
	nodes[x].instk=1;
	int to_;
	for(int y=0; y<nodes[x].to.size(); y++)
	{
		to_=nodes[x].to[y];
		if(nodes[to_].dfn==0)//若没访问过 
		{
			tarjan(to_);//深搜
			nodes[x].low=min(nodes[x].low,nodes[to_].low);//更新
		}
		else if(nodes[to_].instk)//若y已访问且在栈中 
		{
			nodes[x].low=min(nodes[x].low,nodes[to_].dfn);//更新
		}
	}
	if(nodes[x].dfn==nodes[x].low)//若这个节点能链接到自己说明是一个SCC的根 
	{
		int y;
		cnt++;
		do//将节点出栈
		{
			y=stk[top--];
			nodes[y].instk=0;
			scc[y]=cnt;
			++siz[cnt];
		}
		while(y!=x);

	}

}
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>m;
	int a,b;
	for(int ww=1;ww<=m;ww++)
	{
		cin>>a>>b;
		nodes[a].to.push_back(b);
	}
	for(int i=1;i<=n;i++)
	{
		if(!nodes[i].dfn)//图可能不联通
		{
			tarjan(i);
		}
	}

	cout<<cnt;
	return 0;

}



啊对了,还有缩点

简单来说就是把一个 SCC 看做一个点来处理,啊应该就是这样,没了


完结·散花!

没想到这样一篇文章要写整整一个小时啊啊啊

标签:缩点,Tarjan,遍历,int,SCC,dfn,low,节点
From: https://www.cnblogs.com/sea-and-sky/p/18368153

相关文章

  • tarjan之LCA学习笔记
    tarjan之LCA学习笔记tarjan算法求LCA可谓是一个极其巧妙的离线算法其本质是利用DFS遍历时产生的DFS序和并查集来在线性的时间复杂度内求出所有询问的结果既然是离线算法,其和在线算法的区别就在与离线算法需要记录下所有查询,对查询进行一定操作来得到更高的效率,而这......
  • 【Tarjan缩点】USACO5.3 校园网Network of Schools】
    [P2746USACO5.3]校园网NetworkofSchools大意:一个图可能有环a:求deg入度为0的点的个数b:至少加多少条边让图所有点可以互相到达思路:看代码#include<cstdio>#include<queue>#include<deque>#include<stack>#include<map>#include<cmath>#include<algorit......
  • 强连通分量tarjan
    引言强连通分量本质上是处理一个有向有环图(如果在这样的图上搞事情可能会死循环)变成一个有向无环图强连通分量上的点可以互相到达所以针对一些问题(我也没搞明白究竟是哪种问题)例如:给你一张有向图,每个点都有一个点权(不是边权了哦),且每一个点都可以经过任意多次,但是点权只......
  • 【Tarjan SCC 加边使得所有图联通 至少选取多少个点能图联通 】Network of Schools加
    [P2812校园网络【USACO]NetworkofSchools加强版大意:1.图G=(V,E)选几个点可以到达所有的点2.连多少条边可以让任意一个点出发到达其他所有点1思路:1.Tarjan跑一遍求SCC那些出度为0的点就是出发的所有点即din0的点的数量2.计算dout0的点的数量和din0的点的数量取max......
  • 【模板】缩点
    \[\Large给出一个图,求出SCC后缩点得到新的图\]做法:Tarjan记录scc然后根据SCC去重新建图#include<cstdio>#include<stack>#include<algorithm>#include<iostream>#include<cstring>#include<vector>#include<queue>#defineepemplace_b......
  • Tarjan 与连通性
    tarjan是一系列与图连通性相关的算法的统称,本文将总结常见的tarjan算法。并配合一定量的练习。无向图求割边割点割点:删掉后让整个图不联通的点。割边(桥):删掉后让整个图不联通的边。搜索树:对图进行dfs时经过的边的集合。容易发现其构成一棵树。搜索树上的边称为树边。时间......
  • Tarjan算法和连通性相关(三)
    上一篇博客我们介绍了割点和桥,本文我们继续学习与连通性有关的一些概念边双连通分量什么是边双连通分量?在一张连通的无向图中,对于两个点u和v,如果无论删去哪条边(只能删去一条)都不能使它们不连通,我们就说u和v边双连通,边双联通分量是极大的边双连通子图怎么求边双连通......
  • Tarjan算法和连通性相关(二)
    上一篇博客我们介绍了强连通分量,本文我们继续学习与连通性有关的一些概念割点什么是割点?对于一个无向图,如果把一个点删除后这个图的极大连通分量数增加了,那么这个点就是这个图的割点我们画个图理解一下:在这个图中,如果我们把3这个点给删除掉,那么这张图就会被拆分成两个部......
  • Tarjan算法与连通性相关(一)
    昨天在打牛客多校的时候遇到了一道与连通性有关的图论题,笔者一眼就看出这题考察的知识点是Tarjan算法,但是笔者上次写Tarjan算法还是三年前的事情(太惭愧了qaq),于是笔者和队友在赛时花了一个小时时间学习了Tarjan算法成功通过了此题,故写下这篇博客进一步加深印象,同时也是分享自己对于......
  • Tarjan 算法及连通性问题
    无向图的连通性dfs树dfs树上存在树边和返祖边,不存在横叉边。割点若一点\(u\)是割点,那么必定存在一个儿子,删去\(u\)后和他的父亲不连通。如果不存在,和\(u\)相连的所有点依然联通,那么连通性不变,不是割点。若是根节点,若有至少\(2\)个儿子那么就是割点。判断一个点不......