Tarjan 缩点,强联通分量
@[toc]
引入
如果说需要对于一个图上进行 $\tt Dp$,但是同一个环上的点无法进行转移怎么办?
我们只能做 $\tt DAG$ 上的 $\tt Dp$,但是有环我们该怎么办?
缩环!缩点!求强联通!
当然我不是说隔壁的带花树。
虽然说所有的代码都是我随手打的,但是都已经经过测试,请放心阅读。
代码基础定义
dfn[x]
表示当前点遍历顺序的编号。low[x]
表示当前点通过其 $\tt dfs$ 的子树以及能通过一条边到达的点,最浅能到达的编号。
强联通分量
定义:
  有向图中能任意到达的极大点集,被称为一个强联通分量。
  对于一张图,我们将其划分成若干个这样的极大点集,每个点集之间的连边关系构成一张 $\color{red}\tt DAG$。
代码实现:
  我们遍历每一个弱联通分量,考虑使用一个栈来维护每个点的进入顺序。
  每次通过其能到达的点来更新 low[p]
。
- 如果说当前的儿子没有被遍历过,先遍历然后通过
low
更新。- 如果说当前的儿子在栈中,直接通过
dfn
更新就好了。
  这个更新就是根据定义来的。
  那么什么时候可以统计一个强联通分量呢?当 dfn[p] = low[p]
的时候,也就是意味着其是该强联通分量 $\tt dfs$ 时候遍历的第一个点,这样可以保证剩下的点都在栈中,我们直接退栈即可。
1 | void tarjan(int p) { |
边双联通分量和桥
注意是在无向联通图中的。
定义:
  桥:割掉当前的边,使得图不连通。
  边双联通分量:一个点集,使得任意两点之间有两条本质不同的路径,本质不同指路径上没有相同的边。
代码实现:
  和之前的强联通分量相似。我们考虑先计算出所有桥,之后直接遍历连通块即可。
  和之前不同的在于,我们需要考虑父亲节点的贡献,我们考虑一条边 $u \to v$ 是桥意味着 low[v] > dfn[u]
。
  具体含义表示其最浅$\color{red}\text{不能}$到达点 $u$,那么割掉 $u \to v$ 的边就不能联通了。
  正是因为这个定义所以需要考虑$\color{red}\text{父亲节点}$,对于父亲节点我们不能让其产生贡献。
1 | int brg[maxn << 1]; // 表示当前边是否为桥 |
  如果说需要求边双,我们直接对于每一个点进行搜索,不走桥边即可。
本质上就是将桥都断开,看联通的极大点集。
点双联通分量和割点
注意是在无向联通图中的。
定义:
  割点:删除了该点,使得图不连通。
  点双联通分量:点集中任意两点间,存在两条本质不同路径,本质不同指不经过相同的点和边(除了这两个点)。
代码实现
  回忆一下我们边双的求法,判定条件是 dfn[p] < low[to]
,但是我们割点不同。
  即使儿子能到达当前点,但是我们割掉当前点之后还是不连通,所以是 dfn[p] <= low[to]
。
  可以发现这时我们就不需要考虑父亲节点是否产生贡献了。
  我们可以直接在条件合法的时候将儿子节点的 $\tt dfs$ 树上的节点都加入,同时加入当前节点 $u$。
  $\Large\color{red}\text{注意:}$
  同一个点可能属于多个点双联通分量,所以我们最终建立点双图的时候需要再注意。
  我们需要特判,每个开始遍历的节点,必须遍历两个儿子之后才可能称为割点,但是点双是不影响的。
  单个点也是一个 $\tt dcc$。
  我们考虑把割点都单独建立成新的点,之后遍历每一个点双,将其和其子集中的割点连边即可。
1 | int Rt; // Rt 表示遍历的开始节点 |
  下面是重新建图。
1 | int tSz(Sz); |