炸铁路
题目链接:luogu P1656
题目大意
让你找出一个无向连通图的所有割边。
割边是割掉之后图会变成两个连通图。
思路
其实割边就是不在任何环中的边。
那怎么找这些边呢,我们考虑用 tarjan 来找。
那如果你遇到了一个之前没有到过的点,然后继续递归完之后回来更新 \(low_v\) 的时候,我们再多判断一下 \(low_v\) 值是否小于这个点的 \(dfn\),如果是,那就说明没有更新到,那就不在环中。(因为在环中就会有更小的点)
然后这么找就好啦。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node {
int to, nxt;
}e[10003];
struct wr {
int x, y;
}ans[10003];
int n, m, dfn[151], low[151];
int le[151], KK, x, y, an, tmp;
bool br[10003];
void add(int x, int y) {
e[++KK] = (node){y, le[x]}; le[x] = KK;
e[++KK] = (node){x, le[y]}; le[y] = KK;
}
void tarjan(int now, int edge) {
dfn[now] = low[now] = ++tmp;
for (int i = le[now]; i; i = e[i].nxt)
if (!dfn[e[i].to]) {
tarjan(e[i].to, i);
low[now] = min(low[now], low[e[i].to]);
if (low[e[i].to] > dfn[now]) br[i] = br[i ^ 1] = 1;//这个就是它不在环中
}
else if (i != (edge ^ 1)) low[now] = min(low[now], dfn[e[i].to]);
}
bool cmp(wr x, wr y) {
if (x.x != y.x) return x.x < y.x;
return x.y < y.y;
}
int main() {
scanf("%d %d", &n, &m); KK = 1;
for (int i = 1; i <= m; i++) {
scanf("%d %d", &x, &y);
add(x, y);
}
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i, 0);
for (int i = 2; i <= KK; i += 2)
if (br[i]) {
an++;
ans[an] = {e[i].to, e[i ^ 1].to};
if (ans[an].x > ans[an].y) swap(ans[an].x, ans[an].y);
}
sort(ans + 1, ans + an + 1, cmp);
for (int i = 1; i <= an; i++) printf("%d %d\n", ans[i].x, ans[i].y);
return 0;
}