【luogu P1656】炸铁路(求割边)(Tarjan)

炸铁路

题目链接: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;
}
posted @ 2021-09-30 17:22  あおいSakura  阅读(55)  评论(0编辑  收藏  举报