割点,割边
割边
1.割边的定义:割边又叫做桥,如果去掉某条边会使得整张图的连通性变大,那么这条边就叫做割边。
2.求割边的思路:求割边我们使用tarjan算法,假设当前的算法不允许根据走返祖边用祖先的dfn值来更新自己的low值,那么如果有一个点有一个邻点的low值比他的dfn值还要小,就假设这两个点为u和v,那么如果 \(low[v] > low[u]\),那么就说明v如果不走(u, v)这条边的话,无论如何也走不到u前面的点了,那么边(u, v)很显然就是割点。
3.代码如下(这个代码是可以处理重边的,而且不用前向星)
void solve() {
int n, m;cin >> n >> m;
vvpi g(n + 1, vpi());
for(int i = 1; i <= m; i++) {
int u, v;cin >> u >> v;
g[u].push_back({v, i << 1});
g[v].push_back({u, i << 1 | 1});
}
vpi bridge;
vi dfn(n + 1), low(n + 1);
int tot = 0;
function<void(int, int)> tarjan = [&](int u, int fa) -> void {
dfn[u] = low[u] = ++tot;
for(auto [v, id] : g[u]) {
if(id == (fa ^ 1)) continue;
if(!dfn[v]) {
tarjan(v, id);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u]) {
if(u < v) bridge.push_back({u, v});
else bridge.push_back({v, u});
}
} else low[u] = min(low[u], dfn[v]);
}
};
tarjan(1, 0);
sort(bridge.begin(), bridge.end());
for(auto [i, j] : bridge) {
cout << i << " " << j << "\n";
}
}
割点
1.割点的定义:和割边相同,割点是去掉以后能增加图的连通性的点。
2.割点的求法:如果当前的点是割点并且不是根结点,那么直接根据 \(low[v] >= dfn[u]\) 就可以判断出某个点是不是割点。但是如果某个点是根节点,那么就需要两个这样的结点才能判断,为什么呢?因为如果有一个子节点满足上面那个条件,那么他的所有子节点其实都满足这个条件,但如果只有一个子节点的话,删掉一个其实连通性并没有增加。
3.代码如下:
void solve() {
int n, m;cin >> n >> m;
vvi g(n + 1, vi());
for(int i = 1; i <= m; i++) {
int u, v;cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
set<int> cut;
vi dfn(n + 1), low(n + 1);
int tot = 0;
function<void(int, int)> tarjan = [&](int u, int root) -> void {
dfn[u] = low[u] = ++tot;
int child = 0, flag = 0;
for(auto v : g[u]) {
if(!dfn[v]) {
tarjan(v, root);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) {
child += 1;
if(u != root || child > 1) {
flag = 1;
}
}
} else low[u] = min(low[u], dfn[v]);
}
if(flag) cut.insert(u);
};
for(int i = 1; i <= n; i++) {
if(!dfn[i]) tarjan(i, i);
}
cout << cut.size() << "\n";
for(auto i : cut) {
cout << i << " ";
}
cout << "\n";
}