tarjan 缩点笔记

强联通分量

对于图中的两个点 u u u v v v,若分别存在一条路径使得 u → v , v → u u\to v,v\to u uv,vu,则称 ( u , v ) (u,v) (u,v) 强联通。

若对于一张图 G G G 中任意两个点都强联通,则称 G G G 为一个强连通图。

一张图往往由多个强联通子图组成(各个强联通图之间可能会有包含关系),对于那些最大的强联通子图(不存在包含关系),我们称其为强联通分量

不严谨的说,强联通分量就是环。


缩点

若一个图是 D A G DAG DAG (有向无环图),我们可以根据其拓扑序的无后效性,在图上跑各种 d p , d f s dp,dfs dp,dfs,从而找到解。但如果一个图有环,那么后效性就会使我们非常尴尬(环会导致起初遍历过的点的答案发生改变)。

但是往往会有一些非常明显的贪心思路,例如一个环的贡献其实就是等于环上所有点的贡献之和。
因此,我们就可以把一个环 成一个超级点。对图中的所有环进行类似操作,最终图就会转变成一个 D A G DAG DAG,且保留了环的有效贡献,此时我们就可以在图中放心跑各种无后效性的算法。


Tarjan

  • 横向边:若 u → v u\to v uv,且 v → z v\to z vz z ↛ u z\not\to u zu,则显然 z z z 不与 u u u 在同一个强联通分量中,称 v → z v\to z vz 为横向边。
  • 后向边:若 u → v u\to v uv,且 v → z v\to z vz z → u z\to u zu,则显然 u , v , z u,v,z u,v,z 在同一个强联通分量中,称 v → z v\to z vz 为后向边。

显然,横向边不构成一个包含 u u u 的环,后向边反之。因此,找强联通分量即为找到最外层的关于 u u u 的后向边。

我们可以用 d f s dfs dfs 中的栈来实现上述过程,因为 d f s dfs dfs 遍历的是一条由可以到达 u u u 的点和 u u u 可以到达的点组成的路径,我们可以借此来快速判断某一点是否能到达 u u u

  • d f n i dfn_i dfni:时间戳,表示 i i i 被遍历到的时间
  • l o w i low_i lowi:表示在所有能到达 i i i 的点集中, i i i 能到达的 d f n dfn dfn 最小的点的遍历时间。即 i i i 所在的强联通分量中被遍历到最早的点的遍历时间。
  • b e l o n g i belong_i belongi:表示 i i i 所在的强联通分量的编号。
  • v i s i vis_i visi:判断点 i i i 是否在栈中的标记,若在,则 i i i 可到达栈中在它之后的所有点。(考虑 d f s dfs dfs 的原理,遍历一条链)

code:

stack<int> p;
void tarjan(int u)
{
	dfn[u]=low[u]=++tt;
	p.push(u);
	vis[u]=1;  //在栈中
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].v;
		if(!dfn[v])  //还未遍历过
		{
			tarjan(v);
			low[u]=min(low[u],low[v]); //尝试用 v 更新 u
		}
		else if(vis[v]) low[u]=min(low[v],low[u]);  //v可以到达u,在同一个强联通中,因此选最早被遍历的
	}
	if(dfn[u]==low[u])  //u点为所在强联通分量中最早被遍历到的点
	{
		scc++;
		while(1)//栈中,u点之后的所有点都是与u在同一个强联通分量中,与u不同的都已经被处理过了弹出了
		{
			int now=p.top();
			//记录当前强联通分量的贡献
			belong[now]=scc;
			vis[now]=0;
			p.pop();
			if(now==u) break;
		}
	}
	return ;
}

参考文献:

初探 tarjan 算法

TarJan,你真的了解吗

强联通分量算法解析

tarjan 详解


例题讲解:

  1. P3387 【模板】缩点

s o l u t i o n solution solution:显然,如果是一个环,那么肯定,把这个环中所有点都遍历一遍是这个环的最优贡献,因此我们按照此贪心利用 tarjan 缩环为点,得到一张 D A G DAG DAG,然后拓扑后跑 d p dp dp 即可。

  1. P2341 受欢迎的奶牛

s o l u t i o n solution solution
首先,两头明星奶牛 u , v u,v u,v 肯定在同一强联通分量中。
证明:根据明星奶牛的定义, ∀ z ∈ G , z → u \forall z\in G ,z\to u zG,zu ∀ z ∈ G , z → v \forall z\in G ,z\to v zG,zv,因此 u → v u\to v uv v → u v\to u vu,所以 u , v u,v u,v 在同一个强联通分量中。

第二,明星奶牛所在的强联通分量是不会有出边的。
证明:考虑明星奶牛所在的强联通分量为 u u u,有出边为 u → v u\to v uv。由于所有点都可以到达 u u u,因此所有点都可以经过 u u u 到达 v v v,也就是强联通分量 v v v 也是由明星奶牛构成的,但这与我们上一条总结的结论矛盾,因此明星奶牛所在的强联通分量是不会有出边的。

有了这两条结论,做法明了:缩点后找出边为零的点,若改点唯一,则答案为该强联通分量的大小,否则不存在明星奶牛。

  1. P1073 [NOIP2009 提高组] 最优贸易

显然环中的点任意可达,因此这个环买入的最小值为所有点的 min ⁡ \min min ,出售的最大值为所有点的 max ⁡ \max max,以此来缩点,更新强联通分量的权值,在 D A G DAG DAG 上拓扑跑 d p dp dp 即可。

f m i n i fmin_i fmini 为到达 i i i 点时的最小买入价格,则有 f m i n i = min ⁡ ( f m i n j , a i ) fmin_i=\min(fmin_j,a_i) fmini=min(fminj,ai),其中 j j j 为可以到达 i i i 的点, a i a_i ai i i i 点(实际上是一个强联通分量)的最小买入值。

f i f_i fi 为到达 i i i 点的最大利润,则有 f i = max ⁡ ( f j , b i − f m i n i ) f_i=\max(f_j,b_i-fmin_i) fi=max(fj,bifmini),其中 j j j 为可以到达 i i i 的点, b i b_i bi i i i 点(实际上是一个强联通分量)的最大出售值。

答案即为 f n 点 所 在 的 强 联 通 分 量 编 号 f_{n点所在的强联通分量编号} fn

你可能感兴趣的:(强连通分量,拓扑,算法,c++,tarjan)