tarjan相关

tarjan求强连通分量

思想

普通的tarjantarjan就是用来找有向图的强连通分量
也就是在一个有向图中找一个尽可能大的图使其中的每一个点都可到达图中其他任意一点
这个东西tarjantarjan可以O(n)O(n)时间dfsdfs一遍求出

对于图的dfsdfs序,如果有一条有向边指向已遍历过的点,那么这个点到它的当前这个儿子节点形成强连通

就像这样,1,3,21,3,2形成强连通
tarjan相关

但是只是强连通而已,不一定是强连通分量
如果一个节点的子孙有指向它的祖先节点的边,那么一定可以形成一个更大的强连通图
换句话说,如果一个节点的子孙没有指向它的祖先的边、只有指向它的边,那么这个点和它的子孙构成强连通分量

就像这样,蓝色的是强连通图,红色的却是强连通分量
tarjan相关

那么我们只需要知道一个节点的子孙中有没有连回它祖先就可以了
那么就用一个栈记录一下


具体实现

具体实现要弄几个数组来搞tarjantarjan

  • dfn[x]dfn[x]表示xx点在dfsdfs中是第几个被遍历到的

  • low[x]low[x]表示xx点与它的子孙节点中,dfndfn最小的值

  • stack[x]stack[x]表示当前所有可能构成强连通分量的点

  • bz[x]bz[x]表示xx点是否在stackstack

那么一个tarjantarjan过程是这样的

  • 首先dfn[x]=low[x]=++totdfn[x]=low[x]=++tot,并把xx入栈

  • xx能到达的所有点,如果没有访问过,那就dfsdfs该点,并把low[x]low[x]取两者的minmin

  • 如果访问过且该点在栈中那么low[x]low[x]也取两者的minmin

  • 最后如果dfn[x]==low[x]dfn[x]==low[x],说明子孙节点和xx构成强连通分量了,给它们全染上同一种颜色就好了

  • 为什么这么做的原因我不想说


code

O3 inline void tarjan(ll x)
{
	dfn[x]=low[x]=++tot;
	bz[x]=1,stack[++top]=x;
	rep(i,x)
	{
		if (!dfn[tov[i]])
		{
			tarjan(tov[i]);
			low[x]=min(low[x],low[tov[i]]);
		}
		else
		{
			if (bz[tov[i]])low[x]=min(low[x],low[tov[i]]);
		}
	}
	if (dfn[x]==low[x])
	{
		color[x]=++sum,bz[x]=0;
		while (stack[top]!=x)
		{
			color[stack[top]]=sum;
			bz[stack[top--]]=0;
		}
		--top;
	}
}

tarjan缩点

如果用tarjantarjan缩点,相当于把某个强连通分量里的点的权值加起来或什么的,看成一个超级点
做完以后整张图通常是一棵树或是一个DAGDAG
这个思想比较重要
具体的自己手玩


tarjan找点双、边双