tarjan求强连通分量
思想
普通的tarjantarjantarjan就是用来找有向图的强连通分量的
也就是在一个有向图中找一个尽可能大的图使其中的每一个点都可到达图中其他任意一点
这个东西tarjantarjantarjan可以O(n)O(n)O(n)时间dfsdfsdfs一遍求出
对于图的dfsdfsdfs序,如果有一条有向边指向已遍历过的点,那么这个点到它的当前这个儿子节点形成强连通
就像这样,1,3,21,3,21,3,2形成强连通
但是只是强连通而已,不一定是强连通分量
如果一个节点的子孙有指向它的祖先节点的边,那么一定可以形成一个更大的强连通图
换句话说,如果一个节点的子孙没有指向它的祖先的边、只有指向它的边,那么这个点和它的子孙构成强连通分量
就像这样,蓝色的是强连通图,红色的却是强连通分量
那么我们只需要知道一个节点的子孙中有没有连回它祖先就可以了
那么就用一个栈记录一下
具体实现
具体实现要弄几个数组来搞tarjantarjantarjan
-
dfn[x]dfn[x]dfn[x]表示xxx点在dfsdfsdfs中是第几个被遍历到的
-
low[x]low[x]low[x]表示xxx点与它的子孙节点中,dfndfndfn最小的值
-
stack[x]stack[x]stack[x]表示当前所有可能构成强连通分量的点
-
bz[x]bz[x]bz[x]表示xxx点是否在stackstackstack中
那么一个tarjantarjantarjan过程是这样的
-
首先dfn[x]=low[x]=++totdfn[x]=low[x]=++totdfn[x]=low[x]=++tot,并把xxx入栈
-
找xxx能到达的所有点,如果没有访问过,那就dfsdfsdfs该点,并把low[x]low[x]low[x]取两者的minminmin
-
如果访问过且该点在栈中那么low[x]low[x]low[x]也取两者的minminmin
-
最后如果dfn[x]==low[x]dfn[x]==low[x]dfn[x]==low[x],说明子孙节点和xxx构成强连通分量了,给它们全染上同一种颜色就好了
-
为什么这么做的原因我不想说
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缩点
如果用tarjantarjantarjan缩点,相当于把某个强连通分量里的点的权值加起来或什么的,看成一个超级点
做完以后整张图通常是一棵树或是一个DAGDAGDAG
这个操作比较重要
具体的自己手玩