struct CutvertexBridge {
static const int MAXN = 2e5 + 10;
static const int MAXM = 2e6 + 10;
int n, m, t;
struct Edge {
int v, nxt;
} e[MAXM * 2];
int h[MAXN];
int dfn[MAXN], low[MAXN];
bool cut[MAXN], brg[MAXM];
void Init(int n) {
this->n = n, m = 1;
for(int i = 1; i <= n; ++i)
h[i] = 0;
}
void AddEdge(int u, int v) {
if(u == v) {
m += 2;
return;
}
e[++m] = {v, h[u]}, h[u] = m;
e[++m] = {u, h[v]}, h[v] = m;
}
void dfs1(int u, int root) {
dfn[u] = low[u] = ++t;
for(int i = h[u], cnt = 0; i; i = e[i].nxt) {
int v = e[i].v;
if(!dfn[v]) {
dfs1(v, root);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) {
if(u != root || ++cnt > 1)
cut[u] = 1;
}
} else
low[u] = min(low[u], dfn[v]);
}
}
void dfs2(int u, int from) {
dfn[u] = low[u] = ++t;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if(!dfn[v]) {
dfs2(v, i ^ 1);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u])
brg[i / 2] = 1;
} else if(i != from)
low[u] = min(low[u], dfn[v]);
}
}
void GetCutvertexs() {
t = 0;
for(int i = 1; i <= n; ++i)
dfn[i] = low[i] = cut[i] = 0;
for(int i = 1; i <= n; ++i) {
if(!dfn[i])
dfs1(i, i);
}
}
void GetBridges() {
t = 0;
for(int i = 1; i <= n; ++i)
dfn[i] = low[i] = 0;
for(int i = 2; i <= m; i += 2)
brg[i / 2] = 0;
for(int i = 1; i <= n; ++i) {
if(!dfn[i])
dfs2(i, 0);
}
}
} cb;
验证链接:[洛谷P3388 割点] | [HDU4738 Caocao's Bridges]
割点和桥都和dfs搜索树非常相关,可以根据搜索树的树形dp做文章(统计子树大小之类),这个时候桥可能应该用点v表示(易知若(u,v)是桥,那么v到其搜索树的父节点u的边唯一)。
不存在割点的无向连通图叫“点双连通图”,不存在桥的无向连通图叫“边双连通图”