删除这个点后, 图的联通块数量变多.
D F S DFS DFS 时, 设当前点为 k k k, l o w [ ] low[] low[] 为最高祖先, d f n [ ] dfn[] dfn[] 为 d f s dfs dfs 序, t o to to 为 k k k 直接相连的点,
-
若 t o to to 点被访问过, 则说明 k k k 有向上的边, l o w [ k ] = m i n ( l o w [ k ] , d f n [ t o ] ) low[k]=min(low[k], dfn[to]) low[k]=min(low[k],dfn[to])
-
否则沿着 t o to to 继续递归, 回溯时 l o w [ k ] = m i n ( l o w [ k ] , l o w [ t o ] ) low[k] = min(low[k], low[to]) low[k]=min(low[k],low[to]),
若 l o w [ t o ] low[to] low[to] 在 k k k 下方, 说明切掉 k k k, t o to to 会在下方形成一个独立的环,- 若 k k k 不为根节点, k k k 为 割点 ,
- 若 k k k 为根节点, 必须有两个以上儿子才算是割点 .
void Tarjan(int k){
low[k] = dfn[k] = ++ tim;
int cnt = 0, siz = 0;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(dfn[to]) low[k] = std::min(low[k], dfn[to]);
else {
siz ++;
Tarjan(to);
low[k] = std::min(low[k], low[to]);
if(low[to] >= dfn[k]){
cnt ++;
if(k != root || cnt > 1) cut[k] = 1;
}
}
}
}
将边双联通分量缩点 .
D
F
S
DFS
DFS 时, 定义同上, 每次将当前节点压入 栈 中,
当
l
o
w
[
k
]
=
d
f
n
[
k
]
low[k]=dfn[k]
low[k]=dfn[k] 时, 说明
k
k
k节点以下在 栈 中的元素共同构成一个 强联通分量,
此时弹出 栈 内元素, 得到新的环.
与上方判环的方式不同的是, 这里开了个
i
n
_
s
t
k
[
]
in\_stk[]
in_stk[] 数组, 来判断是否在环内.
注意: 只有在栈中, 才能用来更新 l o w low low
void Tarjan(int k){
Dfn[k] = low[k] = ++ t_num;
stk.push(k);
in_stk[k] = 1;
for(int i = head[k]; i; i = edge[i].next){
int t = edge[i].to;
if(!Dfn[t]) Tarjan(t), low[k] = std::min(low[k], low[t]); //WRONG #1
else if(in_stk[t]) low[k] = std::min(low[k], Dfn[t]);
}
if(Dfn[k] == low[k]){
block[k] = ++ block_num;
block_size[block_num] ++;
while(stk.top() != k){
block[stk.top()] = block_num;
block_size[block_num] ++;
in_stk[stk.top()] = 0;
stk.pop();
}
in_stk[k] = 0;
stk.pop();
}
}
将点双联通分量缩点 .
D F S DFS DFS 时, 设当前节点为 k k k, 连向的节点为 t o to to,
- 若 t o to to 访问过且 d f s dfs dfs序比 k k k 小, 说明 t o to to 和 k k k 为一个环的首尾, 将边 k , t o k, to k,to 加入栈中 .
- 若
t
o
to
to 没有访问过, 先将边
k
,
t
o
k, to
k,to 加入栈中, 递归处理
t
o
to
to, 然后检查
l
o
w
[
t
o
]
low[to]
low[to] 是否大于等于
d
f
n
[
k
]
dfn[k]
dfn[k],
若小于, 则不用管, 会在 l o w [ t o ] low[to] low[to] 处通过弹栈操作处理掉 首 为 l o w [ t o ] low[to] low[to] 的环,
若大于等于, 说明 t o to to 可能在 k k k 以下构成了环, 也可能与 k k k 在同一个环内,- 对第一种情况, k k k 和 t o to to 通过弹栈操作仍然能构成点双联通 .
- 对第二种情况, k k k 和 t o to to 在 D F S DFS DFS k k k 这层可以通过弹栈操作找出环 .
void Tarjan(int k, int fa){
low[k] = dfn[k] = ++ tim;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(!dfn[to]){
stk.push(k), stk_2.push(to);
Tarjan(to, k), low[k] = std::min(low[k], low[to]);
if(low[to] < dfn[k]) continue ;
bk_cnt ++;
while(!stk.empty()){
int tp = stk.top(), tu = stk_2.top();
bk[bk_cnt].pb(tp), bk[bk_cnt].pb(tu);
stk.pop(), stk_2.pop();
if(tp == k && tu == to) break ;
}
}else if(dfn[to] < dfn[k] && to != fa){
stk.push(k), stk_2.push(to);
low[k] = std::min(low[k], dfn[to]);
}
}
}