Tarjan求割点、割边的具体图文看https://www.cnblogs.com/nullzx/archive/2017/12/04/7968110.html
Tarjan两点间的必经点、必经边的大概思路:
a->b的必经点:a开始进行dfs,求出割点,判断割点所在分支是否包含点b。若包含,说明割点将a、b分为两个连通分支,为必经点;反之不是。
a->b的必经边:a开始进行dfs,求出割边,判断割边两端点中,远离a端点的分支是否包含b。若包含,说明割边将a、b分为两个连通分支,为必经边;反之不是。
tarjan求割点,割点的判断分为根节点和非根节点判断:
根节点:当有2个以上分支 ,必为割点;
子节点:存在一子节点v,u->v若有low[v] >= dnf[u] ,即子节点不能访问到,则u为割点;
题目:https://www.luogu.com.cn/problem/P3388
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<string>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<vector>
#include<iomanip>
#include<bitset>
#include<stack>
using namespace std;
const int maxn = 200005;
int cut[maxn], dfn[maxn], low[maxn], head[maxn], nxt[maxn], to[maxn], edge[maxn], cnt, idx;
void add(int u, int v, int w = 0) {
nxt[++cnt] = head[u]; to[cnt] = v; head[u] = cnt; edge[cnt] = w;
}
void tarjan(int u, int p) {
int child = 0;
dfn[u] = low[u] = ++idx;
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u] && p != -1)
cut[u] = true;
if (p == -1)
child++;
}
else if (v != p)
low[u] = min(low[u], dfn[v]);
}
if (p == -1 && child >= 2)
cut[u] = true;
}
int main() {
int n, m, ans = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i])
tarjan(i, -1);
}
for (int i = 1; i <= n; i++) if (cut[i]) ans++;
printf("%d\n", ans);
for (int i = 1; i <= n; i++) if (cut[i]) printf("%d ", i);
return 0;
}
tarjan求割边:
存在一子节点v,u->v若有low[v] > dnf[u],则uv为割边;注意low[v] = dnf[u]并不是割边,如重边的情况。
http://hihocoder.com/problemset/problem/1183
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<string>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<vector>
#include<iomanip>
#include<bitset>
#include<stack>
using namespace std;
const int maxn = 200005;
int cutpoint[maxn], cutedge[maxn], idx[maxn], dfn[maxn], low[maxn], head[maxn], nxt[maxn], to[maxn], cnt, cnt2;
unordered_map<int, vector<int>> mp;
void add(int u, int v, int i) {
nxt[++cnt] = head[u]; to[cnt] = v; idx[cnt] = i; head[u] = cnt;
}
void tarjan(int u, int p) {
int child = 0;
dfn[u] = low[u] = ++cnt2;
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
//非根节点的割点判断
if (low[v] >= dfn[u] && p != -1)
cutpoint[u] = true;
//割边判断
if (low[v] > dfn[u]) {
cutedge[idx[i]] = true;
mp[idx[i]] = { u, v };
}
if (p == -1)
child++;
}
else if(v != p)
low[u] = min(low[u], dfn[v]);
}
//根节点的割点判断
if (p == -1 && child >= 2)
cutpoint[u] = true;
}
int main() {
int n, m, ans = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y, i);
add(y, x, i);
}
tarjan(1, -1);
for (int i = 1; i <= n; i++)
if (cutpoint[i])
printf("%d ", i);
printf("\n");
for (int i = 1; i <= m; i++)
if (cutedge[i])
printf("%d %d\n", mp[i][0], mp[i][1]);
return 0;
}