说明
写这篇文章,只是起一个mark的作用,避免自己以后遗忘出现错漏,或者每次都要现推一遍
想到有东西就会来补充,也欢迎读者补充
对于那些学算法的就不要看了233
为了方便理解,本文牺牲严谨,尽量以形象的语言说明
有向图
强联通分量
这个很常用的,打起来也很方便
f o r e a c h ( v , u ) ∈ E for\ each (v,u)\in E for each(v,u)∈E
u已访问(u已经在栈中(防止横叉边)),则 l o w [ v ] = m i n ( l o w [ v ] , d f n [ u ] ) ( 返 祖 边 ) low[v]=min(low[v],dfn[u])(返祖边) low[v]=min(low[v],dfn[u])(返祖边)
u未访问, l o w [ v ] = m i n ( l o w [ v ] , l o w [ u ] ) ( 树 枝 边 ) low[v]=min(low[v],low[u])(树枝边) low[v]=min(low[v],low[u])(树枝边)
缩点
把一个强连通分量内的点缩在一起
缩完点之后的图一定是DAG
求桥边(必经边)、割点(必经点)
DAG
单源单汇
方法很多,对于边
(
u
,
v
)
(u,v)
(u,v),简单做法是用拓扑DP求出S到u,v到T路径总数,判断乘积是否等于S到T路径总数
必经点类似
单源多汇
可以用一个叫支配树的数据结构
并没有仔细去学,听了一下WerkeyTom的口胡
构建的支配树中,fa[v]是v的最近必经点
当v入度为1时,fa[v]显然。。。
否则,
f
a
[
v
]
=
l
c
a
(
∀
(
u
,
v
)
∈
E
f
a
[
u
]
)
fa[v]=lca(\forall_{(u,v)\in E}\ fa[u])
fa[v]=lca(∀(u,v)∈E fa[u])(用心感受)
必经边的话,只要把边
(
u
,
v
)
(u,v)
(u,v)拆成边
(
u
,
w
)
,
(
w
,
v
)
(u,w),(w,v)
(u,w),(w,v),转化成判断w是否为必经点
有环图
不能简单地把环缩成点,因为环里面的点、边可能也是必经的
继续考虑拆边(和上面一样),只需要用Tarjan求必经点
无向图
无向图的话其实类似,但是注意,算法中,要强制 ( u , v ) (u,v) (u,v)只会被单向的经过一次
割点&点双联通分量
割点:删掉该点,连通图断开
点双连通分量:将该连通分量中任意一个点删除,其余点两两可达
若对于
(
v
,
u
)
(v,u)
(v,u),若
l
o
w
[
u
]
>
=
d
f
n
[
v
]
low[u]>=dfn[v]
low[u]>=dfn[v],则
v
v
v为割点,v和u子树内的点在一个点双内
注意,割点可能同时存在于多个点双中
这样求完,最后栈中剩下的点构成一个点双
桥&边双联通分量
桥:删掉该边,连通图断开
边双连通分量:将该连通分量中任意一条边删除,任意点两两可达
若对于
(
v
,
u
)
(v,u)
(v,u),
l
o
w
[
u
]
>
d
f
n
[
v
]
low[u]>dfn[v]
low[u]>dfn[v],则
(
v
,
u
)
(v,u)
(v,u)为桥,u及其子树在一个边双内
若
l
o
w
[
v
]
=
d
f
n
[
v
]
low[v]=dfn[v]
low[v]=dfn[v],则
(
f
a
[
v
]
,
v
)
(fa[v],v)
(fa[v],v)为桥,v及其子树在一个边双内
桥不属于任何一个边双
缩点
一般是把边双的点缩在一起
缩完点之后形成一棵树