tarjan求桥
定义
1、强连通:
在有向图G中,如果两个顶点u,v间有一条从u到v的有向路径,同时还有一条从v到u的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向非强连通图的极大强连通子图,称为强连通分量。
2、桥:
在图中,如果删除一条边使得这个图分成了两个图(没有其他边连通),我们就称这条边为桥
例题
题目大意:
在一个图中,炸掉一条边,使得某两个点没有路径相连。
思路:
题意要求出图上所以的桥
我们可以先设出两个数组:
1、
d
f
n
i
dfn_i
dfni 表示
i
i
i点的时间戳(点
i
i
i是第几个被访问的点)
2、
l
o
w
i
low_i
lowi 与点
i
i
i能到达所有点的时间戳的最小值(不包括
i
i
i的父亲)
当原图上的边连接两个端点
f
a
t
h
e
r
和
s
o
n
father和son
father和son:
显然,当
d
f
n
f
a
t
h
e
r
<
l
o
w
s
o
n
dfn_{father} < low_{son}
dfnfather<lowson时这条边为桥
code:
#include<bits/stdc++.h>
#define LL long long
#define inl inline
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
using namespace std;
const int N = 155 , M = 5005;
int dfn[N] , low[N] , n , m , ct , cnt = 1 , hd[N] , flag[M << 1];
struct E {
int to , fr , nt;
}e[M << 1];
struct Pr {
int fr , to;
}pr[M << 1];
inl void add (int x , int y) {
e[++cnt].to = y , e[cnt].fr = x , e[cnt].nt = hd[x] , hd[x] = cnt;
}
inl int read () {
int val = 0, fu = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') fu = -1;
ch = getchar ();
}
while (ch >= '0' && ch <='9') {
val = val * 10 + (ch - '0');
ch = getchar ();
}
return val * fu;
}
inl void dfs (int x , int fa) {
dfn[x] = low[x] = ++ct;
int y;
for (int i = hd[x] ; i ; i = e[i].nt) {
y = e[i].to;
if (!dfn[y]) {
dfs (y , x);
low[x] = min (low[x] , low[y]);
if (low[y] > dfn[x]) {
flag[i] = flag[i ^ 1] = 1;
}
}
else if (y != fa) {
low[x] = min (low[x] , dfn[y]);
}
}
}
inl bool comp (Pr x , Pr y) {
if (x.fr < y.fr) return 1;
else if (x.fr == y.fr && x.to < y.to) return 1;
else return 0;
}
int main () {
int u , v;
n = read () , m = read ();
fu(i , 1 , m) {
u = read () , v = read ();
add (u , v);
add (v , u);
}
fu(i , 1 , n) {
if (dfn[i]) continue;
dfs (i , 0);
}
ct = 0;
for (int i = 2 ; i <= cnt ; i += 2) {
int x = min (e[i].fr , e[i].to) , y = max (e[i].fr , e[i].to);
if (flag[i]) {
pr[++ct].fr = x , pr[ct].to = y;
}
}
sort (pr + 1 , pr + ct + 1 , comp);
fu(i , 1 , ct) {
printf ("%d %d\n" , pr[i].fr , pr[i].to);
}
return 0;
}