2019年7月训练(贰)(暂)

 

2019-07-25

luogu P2341 [HAOI2006]受欢迎的牛+Tarjan小复习

 

Tarjan

何为强联通分量

有向图强连通分量:在有向图GG中,如果两个顶点V_i,V_jVi,Vj间(V_i>V_jVi>Vj)有一条从V_iViV_jVj的有向路径,同时还有一条从V_iViV_jVj的有向路径,则称两个顶点强连通。如果有向图GG的每两个顶点都强连通,称GG是一个强连通图。有向图的极大强连通子图,称为强连通分量。 ——百度百科

事实上,你大概可以理解为:如果一个图的子图中,任意两点可以相互到达,那么这就组成了一个强联通分量。

如果还不理解怎么办?没关系,我们上图像来理解

如图,在这个有向图中,一共有\{1,2,3,4\},\{5\},\{6\}{1,2,3,4},{5},{6}三个强联通分量

如何求强联通分量

我们需要两个非常重要的数组,在这里先说明一下

1.dfn1.dfn,表示这个点在dfsdfs时是第几个被搜到的。

2.low2.low,表示这个点以及其子孙节点连的所有点中dfndfn最小的值

3.stack3.stack,表示当前所有可能能构成是强连通分量的点。

4.vis4.vis,表示一个点是否在stackstack数组中。

我们使用tarjantarjan的方法 (1)、首先初始化dfn[u]=low[u]=dfn[u]=low[u]=第几个被dfsdfs到

(2)、将uu存入stack[ ]stack[]中,并将vis[u]vis[u]设为truetrue

(3)、遍历uu的每一个能到的点,如果这个点dfn[ ]dfn[]为00,即仍未访问过,那么就对点vv进行dfsdfs,然后low[u]=min\{low[u],low[v]\}low[u]=min{low[u],low[v]}

(4)、假设我们已经dfsdfs完了uu的所有的子树那么之后无论我们再怎么dfsdfs,uu点的lowlow值已经不会再变了。

至此,tarjantarjan完美结束

那么如果dfn[u]=low[u]dfn[u]=low[u]这说明了什么呢?

再结合一下dfndfn和lowlow的定义来看看吧

dfndfn表示uu点被dfsdfs到的时间,lowlow表示uu和uu所有的子树所能到达的点中dfndfn最小的。

这说明了uu点及uu点之下的所有子节点没有边是指向u的祖先的了,即我们之前说的uu点与它的子孙节点构成了一个最大的强连通图即强连通分量

此时我们得到了一个强连通分量,把所有的u点以后压入栈中的点和u点一并弹出,将它们的vis[ ]vis[]置为falsefalse,如有需要也可以给它们打上相同标记(同一个数字)


Q:Qdfndfn可以理解,但为什么lowlow也要这么做呢?

A:A:因为low的定义如上,也就是说如果没有子孙与u的祖先相连的话,dfn[u]dfn[u]一定是它和它的所有子孙中dfndfn最小的(因为它的所有子孙一定比他后搜到)。

Q:Qstack[]stack[]有什么用?

A:A:如果uu在stackstack中,uu之后的所有点在uu被回溯到时uu和栈中所有在它之后的点都构成强连通分量。

Q:Qlow[ ]low[]有什么用?

A:A:应该能看出来吧,就是记录一个点它最大能连通到哪个祖先节点(当然包括自己)

如果遍历到的这个点已经被遍历到了,那么看它当前有没有在stack[ ]stack[]里,如果有那么low[u]=min\{low[u],low[v]\}low[u]=min{low[u],low[v]}

如果已经被弹掉了,说明无论如何这个点也不能与uu构成强连通分量,因为它不能到达uu

如果还在栈里,说明这个点肯定能到达uu,同样uu能到达他,他俩强联通。

接下来,就是非(sang)(sang)常(xin)(xin)简(bing)(bing)单(kuang)(kuang)的手\%%过程了

从节点11开始DFSDFS,把遍历到的节点加入栈中。搜索到节点u=6u=6时,DFN[6]=LOW[6]DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=vu=v为止,\{6\}{6}为一个强连通分量。

之后返回节点55,发现DFN[5]=LOW[5]DFN[5]=LOW[5],于是我们又找到了一个新的强联通分量\{5\}{5}

返回节点33,继续搜索到节点44,把44加入堆栈。发现节点44向节点11有后向边,节点11还在栈中,所以LOW[4]=1LOW[4]=1。节点66已经出栈,(4,6)(4,6)是横叉边,返回33,(3,4)(3,4)为树枝边,所以LOW[3]=LOW[4]=1LOW[3]=LOW[4]=1。

继续回到节点11,最后访问节点22。访问边(2,4)(2,4),44还在栈中,所以LOW[2]=DFN[4]=5LOW[2]=DFN[4]=5。返回11后,发现DFN[1]=LOW[1]DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量\{1,3,4,2\}{1,3,4,2}。

至此,tarjantarjan算法结束,我们找到了全部的33个强联通分量\{1,2,3,4\},\{5\},\{6\}{1,2,3,4},{5},{6}

以下代码实现:

inline int tarjan(int u) 
{
    low[u]=dfn[u]=++dfn_sum;
    stack[top++]=u;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(dfn(v))
            low[u]=min(low[u],dfn[v]);
        else
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        int now=stack[--top];s_sum++;
        s[u]+=s_sum;
        while(now!=u)
        {
            s[now]=s_num;
            now=s[--top];
        }
    }
}

  


2019-07-25 23:38:32

未完

猜你喜欢

转载自www.cnblogs.com/plzplz/p/11247732.html
今日推荐