tarjan学了老是忘,还容易把边双点双强连通分量搞混,这里写个总结。
定义dfn(x)为x点dfs序,low(x)为该点能通过非树边返回的深度最小祖先的dfn。
一、无向图tarjan:
1.求low(x)
首先将low(x)初始化为dfn(x),对于连着x的边(x,y),若x为y父亲,则先dfs(y),low(x)=min(low(x),low(y)),不然说明为非树边,low(x)=min(low(x),dfn(y))
2.割点和桥
定义桥为删除掉图会分裂成两个及以上子图的边,割点为删除掉图会分裂为两个以上子图的点。
若一条x到子节点y的边是桥,则要满足dfn(x)<low(y),所以dfs判一下即可,要注意与平时dfs(x)传入fa(x)不同这里要传入fa(x)到x的边标号以防重边情况。
割点情况类似,若x是割点,则要存在一个y,使得dfn(x)<=low(y),这里可以不用判断重边情况(最好还是判反正没多大区别),需要注意根是割点的条件较特殊是有两个及以上子节点。
3.点双边双
点双图是一个无向图,其中不存在割点。点双连通分量指原图一个极大点双连通子图(即不存在包含它更大点双子图)。
边双图是一个无向图,其中不存在桥。边双连通分量指原图一个极大边双连通子图(即不存在包含它更大边双子图)。
有一些没啥用的定理:
1.点双图至少满足以下两条件之一:顶点数不超过2;任意两点至少包含在一个简单环(不自交环)中。
2.一张图是边双图当且仅当任意边都包含在至少一个简单环中。
求边双:将桥边删除,每个连通块都是一个边双。而将每个边双看成一个点建新图即可完成边双缩点。
求点双:首先对于孤立点(没有连边的)直接自己作为点双,其余的就在tarjan时维护一个栈,一个点第一次访问时加入栈,当出现dfn(x)<=low(y),从栈顶不断弹,弹出y为止,所有弹出的和x构成割点。之后缩点则将每个割点和包含它的点双连边构成森林。
二、有向图tarjan
一张有向图,若其中任意两个点x,y都既存在x到y路径也存在y到x路径,则其为强连通图,类比点双边双分量可以定义强连通分量。
求强连通分量:维护一个栈记录当前点祖先点集合,然后计算low(x)时与求割点桥差不多,就是一个点要判断是当前点祖先要看它是否在栈中。最后在x点回溯前判断是否有low(x)==dfn(x),如果有则将栈弹到x出栈为止,所有出栈的构成一个强连通分量,缩点则与边双类似。