文章目录
- 代码实现
- 实际应用
- 1.有向图
- 另外:对于缩点之后的DAG的处理
- 2.无向图
- 求法
- 细节
- 细节:
- 目录:
- 1.「POJ 3694」Network
- 2.「2019 ICPC 横滨站」
- 3. P3225 [HNOI2012]矿场搭建
- 4. 一本通 分离的路径
代码实现
所以其实就三个玩意
1.dfn[],low[],indx
2.stack<int>s,bool pd[]
3.scc[],scnt,col[]等我们要求的信息变量名 用处
dfn 记录当前已经访问了几个节点
scc 记录当前已经有了几个强连通分量
dfn[ maxn ] 当前节点是第几个被访问到的
low[ maxn ] 当前节点所能访问到的最小的 dfn[ ]
scc[ maxn ] 记录各个节点所属于的强连通分量编号
s[ maxn ] 存储可能构成强连通分量的节点的栈
top 存储栈顶
那么我们如果要求scc这样一个东西,应该是用什么结构呢?
思想其实并不是很难,就是我们要求SCC,将它放入dfs序中,发现它有一定的特点,那就是在搜索树中的点,一定可以通向祖先,那他们就是一个scc,因为他们可以彼此到达。
所以我们要找最大的scc,那么就是要求出最小的祖先。
所以每个节点不用存下所有祖先,而只用存这一个就够了。
所以有了low[]来存x的搜索树内边和最小的点。
那么肯定是要dfs的。
发现low[]=min(low[son]),min(dfn[to]);
所以dfs回溯递归处理,该问题具有自相似性。
实际应用
可以分为两类
1.有向图
缩点之后变成了一个DAG,那么什么时候应该缩点呢,
例子:
-
“喜欢”是可以传递的——如果A喜欢B,B喜欢C,那么A也喜欢C
-
一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。
-
给定一个 n 个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大,允许多次通过同一个点和边。
-
从S到T,可以重复走,求所有路径中点权最大值与最小值的差。
-
P1407 我们已知n对夫妻的婚姻状况,称第i对夫妻的男方为Bi,女方为Gi。若某男Bi与某女Gj曾经交往过(无论是大学,高中,亦或是幼儿园阶段,i≠j),则当某方与其配偶(即Bi与Gi或Bj与Gj)感情出现问题时,他们有私奔的可能性。不妨设Bi和其配偶Gi感情不和,于是Bi和Gj旧情复燃,进而Bj因被戴绿帽而感到不爽,联系上了他的初恋情人Gk……一串串的离婚事件像多米诺骨牌一般接踵而至。若在Bi和Gi离婚的前提下,这2n个人最终依然能够结合成n对情侣,那么我们称婚姻i为不安全的,否则婚姻i就是安全的。
给定所需信息,你的任务是判断每对婚姻是否安全。
总结 :可以重复走,有传递性(即一个接一个)
另外:对于缩点之后的DAG的处理
1.DAG 一定存在一个或多个入度为0或出度为0的点。
2.可以进行拓扑排序,排序靠后的点不可能到达靠前的点。
3.DAG上的DP(期望等)
4.把入度为0的点与出度为0的点之间连边,会变成一个SCC
2.无向图
点双连通:删去任何一个点仍然连通边双连通:删去任何一条边仍然连通
(下面会简称为“点双和边双”)
另一种定义是,任何两点之间至少存在两条不经过相同中间点(边)的路径
如果不满足双连通性
割点(割顶):删去后原图不连通的顶点集合点双连通:删去任何一个点仍然连通边双连通:删去任何一条边仍然连通割边(桥):删去后原图不连通的边集合
- 边双连通分量:删去所有割边,每个连通块都是边双连通分量
- 一张图的点双连通分量之间可能有公共点,边双连通分量之间不可能有公共点(否则他们整体可以构成一个边双)
- 满足点(边)双连通性的极大子图称为点(边)双连通分量
- 如果一条边左边是一个最大的边双连通子图(边双),右边是另一个最大的边双连通子图(边双),那么这个边就是没有用的,删去,这样剩下的就是边双了
- 点双与割点可能会有公共点。点双和点双之间可能会有公共点
- 割边的两端大多数情况下是割点,除了一边只有一个点的情况
求法
- 割边
- 不需要用到栈,因为不用存储强连通分量。
- 割边一定存在于树边上,否则存在另一条路径。
- 注意low的定义有所变化,有向图中low只代表能到达的在栈中的节点的最小值。但在无向图中,没有栈的限制。
- dfn[u]<low[v]那么(u,v)即为割边
细节
- 注意这里变成了严格小于,因为我们限制了指向父亲的边,那么若low等于dfn的话相当于存在一条回路。
- 但是要进行特判不能让出去的点再次走同一个无向边,否则low[to]必定等于dfn[x],没有意义了,但是还要允许重边访问,所以我们应该记录边的编号。
- 割点
- 对于搜索树中的根节点,儿子数大于等于2即为割点。
- 对于一般点:dfn[u]<=low[v]那么u就是割点
- 但是注意割边两边一般是割点,但是特殊情况下一边只有一个点,那就不是割点了。
细节:
- 引入了rt,son每次tarjan都要重新赋值。
- 小心pd[]=true,cnt++;这样的语句,因为bool值本来可能已赋值过了。
- 分类讨论!!!不要忘了。
目录:
- 「POJ 3694」Network
- 边双缩点变成树,在线查询两点之间边的个数,然后再一次缩点。
- 「2019 ICPC 横滨站」
- 边双在指定无向边的方向之后一定可以变成强连通分量。
- P3225 [HNOI2012]矿场搭建
1.「POJ 3694」Network
在线查询一个无向图,加入一条边后减少了多少条桥。
2.「2019 ICPC 横滨站」
给出一些起点和终点,判定把无向边确定方向之后能否满足每一个起点都可以走到对应的终点。
3. P3225 [HNOI2012]矿场搭建
题意:对于一个矿场可视作无向图,现在已知任意矿场塌陷后,每一个点可以从出口逃出,那么最少在图上建立几个出口及方案数。
那么可以发现删去一个点对于点双仍然联通,所以dfs每一个点双,统计点双中有多少割点。
可以发现,如果将点双视为点,那么图会变成一个树,如果断掉一个割点,相当于断裂一条边,所以只要在叶子节点上建立就可以了。
- 若没有割点则建立一个,方案数为节点数。
- 若有一个割点则需要建立一个在非割点上,方案数为非割点的数目。
- 若有两个割点则不需要建立。
4. 一本通 分离的路径
给出一个无向连通图,现在求出使每个点到其他点都有两条完全不同的路径最少还需要添加几条边。
可以发现这就是一个边双,那么对边双缩点,图会变成一个树,那么只要给两个叶子节点连边再缩点,就可以使新的树叶子数减少二,所以共用(sum-1)/2。
注意各个题解都没有注意到的是,如果两个普通两个叶子相连只能减少一个叶子,因为如果他们的lca有两个儿子就会在他们的lca处新增一个叶子。所对叶子节点的选取是有要求的。