Tarjan算法基于深度优先遍历, 可在O(n)的时间复杂度下处理问题
一. Tarjan算法在无向图上的应用:
1.Tarjan求桥
| struct Tarjan_Bridge |
| { |
| struct Edge |
| { |
| int to, next; |
| }e[maxn << 1]; |
| int head[maxn], tot = 1; |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot); |
| } |
| int dfn[maxn], low[maxn], Time; |
| bool bridge[maxn]; |
| void Tarjan(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time; |
| fort(u) |
| { |
| if(v == fa) continue; |
| if(!dfn[v]) |
| { |
| Tarjan(v, u); |
| low[u] = min(low[u], low[v]); |
| if(low[v] > dfn[u]) |
| bridge[i]= bridge[i ^ 1] = true; |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]); |
| } |
| } |
| return; |
| } |
| void work() |
| { |
| for (int i = 1; i <= m; ++ i) |
| { |
| int u = read(), v = read(); |
| Insert(u, v), Insert(v, u); |
| } |
| for (int i = 1; i <= n; ++ i) |
| { |
| if(! dfn[i]) |
| { |
| Tarjan(i, 0); |
| } |
| } |
| return; |
| } |
| }T_bridge; |
2. Tarjan算法求割点
| struct Tarjan_Cut |
| { |
| struct Edge |
| { |
| int to, next; |
| }e[maxn << 1]; |
| int head[maxn], tot = 1; |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot); |
| } |
| int dfn[maxn], low[maxn], Time; |
| int root; |
| bool cut[maxn]; |
| |
| void Tarjan(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time; |
| int flag = 0; |
| fort(u) |
| { |
| if (v == fa) continue; |
| if(! dfn[v]) |
| { |
| Tarjan(v, u); |
| low[u] = min(low[u], low[v]); |
| if(low[v] >= dfn[u]) |
| { |
| flag ++; |
| if(u != root || flag > 1) |
| cut[u] = true; |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]); |
| } |
| } |
| } |
| } |
| void work() |
| { |
| for (int i = 1; i <= m; ++ i) |
| { |
| int u = read(), v = read(); |
| Insert(u, v), Insert(v, u); |
| } |
| for (int i = 1; i <= n; ++ i) |
| { |
| if(! dfn[i]) |
| { |
| root = i; |
| Tarjan(i, 0); |
| } |
| } |
| return; |
| } |
| }T_cut; |
3. Tarjan算法求e_DCC
| struct Tarjan_e_DCC |
| { |
| struct Edge |
| { |
| int to, next; |
| }e[maxn << 1], g[maxn << 1]; |
| int head[maxn], tot = 1; |
| int headg[maxn], totg = 1; |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot); |
| } |
| void Insertg(int u, int v) |
| { |
| return (void)(g[++ totg].to = v, g[totg].next = headg[u], headg[u] = totg); |
| } |
| int dfn[maxn], low[maxn], Time; |
| int bel[maxn], tot_dcc; |
| bool bridge[maxn]; |
| void Tarjan_bridge(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time; |
| fort(u) |
| { |
| if(v == fa) continue; |
| if(!dfn[v]) |
| { |
| Tarjan_bridge(v, u); |
| low[u] = min(low[u], low[v]); |
| if(low[v] > dfn[u]) |
| bridge[i]= bridge[i ^ 1] = true; |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]); |
| } |
| } |
| return; |
| } |
| void Dfs(int u) |
| { |
| bel[u] = tot_dcc; |
| fort(u) |
| { |
| if(bel[v] || bridge[i]) continue; |
| Dfs(v); |
| } |
| return; |
| } |
| void SD_e_DCC() |
| { |
| for (int i = 2; i <= tot; ++ i) |
| { |
| int u = e[i ^ 1].to, v = e[i].to; |
| if(bel[u] == bel[v]) continue; |
| Insertg(bel[u], bel[v]); |
| } |
| return; |
| } |
| void work() |
| { |
| for (int i = 1; i <= m; ++ i) |
| { |
| int u = read(), v = read(); |
| Insert(u, v), Insert(v, u); |
| } |
| for (int i = 1; i <= n; ++ i) |
| { |
| if(!dfn[i]) |
| { |
| Tarjan_bridge(i, 0); |
| } |
| } |
| for (int i = 1; i <= n; ++ i) |
| { |
| if(! bel[i]) |
| { |
| ++ tot_dcc, Dfs(i); |
| } |
| } |
| SD_e_DCC(); |
| return; |
| } |
| }T_e_DCC; |
Tarjan算法求v_DCC
| struct Tarjan_v_DCC |
| { |
| struct Edge |
| { |
| int to, next; |
| }e[maxn << 1], g[maxn << 1]; |
| int head[maxn], tot = 1; |
| int headg[maxn], totg = 1; |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot); |
| } |
| void Insertg(int u, int v) |
| { |
| return (void)(g[++ totg].to = v, g[totg].next = headg[u], headg[u] = totg); |
| } |
| int dfn[maxn], low[maxn], Time; |
| int stk[maxn], top; |
| int root; |
| bool cut[maxn]; |
| int num, new_id[maxn], bel[maxn]; |
| vector <int> dcc[maxn]; |
| int tot_dcc; |
| |
| void Tarjan(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time; |
| stk[++ top] = u; |
| if(u == root && head[u] == 0) |
| { |
| dcc[++ tot_dcc].push_back(u); |
| return; |
| } |
| int flag = 0; |
| fort(u) |
| { |
| if(v == fa) continue; |
| if(! dfn[v]) |
| { |
| Tarjan(v, u); |
| low[u] = min(low[u], low[v]); |
| if(low[v] >= dfn[u]) |
| { |
| flag ++; |
| if(u != root || flag > 1) cut[u] = true; |
| tot_dcc ++; |
| while(stk[top + 1] != v) |
| { |
| dcc[tot_dcc].push_back(stk[top --]); |
| } |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]); |
| } |
| } |
| } |
| return; |
| } |
| void SD_v_DCC() |
| { |
| num = tot_dcc; |
| for (int i = 1; i <= n; ++ i) |
| { |
| if(cut[i]) |
| { |
| new_id[i] = ++ num; |
| } |
| } |
| for (int i = 1; i <= tot_dcc; ++ i) |
| { |
| for (int j = 0; j < dcc[i].size(); ++ j) |
| { |
| int x = dcc[i][j]; |
| if(cut[x]) |
| { |
| Insertg(i, new_id[x]), Insertg(new_id[x], i); |
| } |
| else |
| { |
| bel[x] = i; |
| } |
| } |
| } |
| return; |
| } |
| void work() |
| { |
| for (int i = 1; i <= m; ++ i) |
| { |
| int u = read(), v = read(); |
| Insert(u, v), Insert(v, u); |
| } |
| for (int i = 1; i <= n; ++ i) |
| { |
| if(! dfn[i]) |
| { |
| Tarjan(i, 0); |
| } |
| } |
| SD_v_DCC(); |
| return; |
| } |
| }T_v_DCC; |
二. Tarjan算法在有向图上的应用:
Tarjan算法求SCC
| struct Tarjan_SCC |
| { |
| struct Edge |
| { |
| int to, next; |
| }e[maxn << 1], g[maxn << 1]; |
| int head[maxn], tot = 1; |
| int headg[maxn], totg = 1; |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot); |
| } |
| void Insertg(int u, int v) |
| { |
| return (void)(g[++ totg].to = v, g[totg].next = headg[u], headg[u] = totg); |
| } |
| int dfn[maxn], low[maxn], Time; |
| int stk[maxn], top; |
| bool inv[maxn]; |
| int tot_scc, bel[maxn]; |
| vector <int> scc[maxn]; |
| |
| void Tarjan(int u) |
| { |
| dfn[u] = low[u] = ++ Time; |
| stk[++ top] = u, inv[u] = true; |
| fort(u) |
| { |
| if(!dfn[v]) |
| { |
| Tarjan(v); |
| low[u] = min(low[u], low[v]); |
| } |
| else if(inv[v]) |
| { |
| low[u] = min(low[u], dfn[v]); |
| } |
| } |
| if(dfn[u] == low[u]) |
| { |
| ++ tot_scc; |
| while(stk[top + 1] != u) |
| { |
| int v = stk[top]; |
| inv[v] = false; |
| bel[v] = tot_scc; |
| scc[tot_scc].push_back(v); |
| } |
| } |
| return; |
| } |
| void SD_SCC() |
| { |
| for (int u = 1; u <= n; ++ u) |
| { |
| fort (u) |
| { |
| if(bel[u] == bel[v]) continue; |
| Insert(bel[u], bel[v]); |
| } |
| } |
| return; |
| } |
| void work() |
| { |
| for (int i = 1; i <= m; ++ i) |
| { |
| int u = read(), v = read(); |
| Insert(u, v); |
| } |
| for (int i = 1; i <= n; ++ i) |
| { |
| if(! dfn[i]) |
| { |
| Tarjan(i); |
| } |
| } |
| SD_SCC(); |
| return; |
| } |
| }T_SCC; |
点击查看代码
| #include <algorithm> |
| #include <iostream> |
| #include <cstring> |
| #include <bitset> |
| #include <cstdio> |
| #include <cmath> |
| #include <queue> |
| |
| using namespace std |
| |
| #define ull unsigned long long |
| #define rint register int |
| #define ll long long |
| #define dd double |
| // #define int long long |
| #define fort(x) for (int i = head[x], v = e[i].to |
| |
| const int INF = 0x3f, INFF = 0x3f3f3f3f, INFFF = 0x7fffffff |
| const int maxn = 3e6 + 5, maxm = 1e3 + 5 |
| |
| int n, m |
| |
| int read() |
| { |
| int x = 0, w = 0 |
| while(ch < '0' || ch > '9'){ if(ch == '-') w = 1 |
| while(ch >= '0' && ch <= '9'){ x = (x << 1) + (x << 3) + (ch ^ '0') |
| if(w) return -x |
| } |
| struct Tarjan_Bridge //无向图 桥 |
| { |
| struct Edge |
| { |
| int to, next |
| }e[maxn << 1] |
| int head[maxn], tot = 1 |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot) |
| } |
| int dfn[maxn], low[maxn], Time |
| bool bridge[maxn] |
| void Tarjan(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time |
| fort(u) |
| { |
| if(v == fa) continue |
| if(!dfn[v]) |
| { |
| Tarjan(v, u) |
| low[u] = min(low[u], low[v]) |
| if(low[v] > dfn[u]) |
| bridge[i]= bridge[i ^ 1] = true |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]) |
| } |
| } |
| return |
| } |
| void work() |
| { |
| for (int i = 1 |
| { |
| int u = read(), v = read() |
| Insert(u, v), Insert(v, u) |
| } |
| for (int i = 1 |
| { |
| if(! dfn[i]) |
| { |
| Tarjan(i, 0) |
| } |
| } |
| return |
| } |
| }T_bridge |
| struct Tarjan_Cut //无向图 割点 |
| { |
| struct Edge |
| { |
| int to, next |
| }e[maxn << 1] |
| int head[maxn], tot = 1 |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot) |
| } |
| int dfn[maxn], low[maxn], Time |
| int root |
| bool cut[maxn] |
| |
| void Tarjan(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time |
| int flag = 0 |
| fort(u) |
| { |
| if (v == fa) continue |
| if(! dfn[v]) |
| { |
| Tarjan(v, u) |
| low[u] = min(low[u], low[v]) |
| if(low[v] >= dfn[u]) |
| { |
| flag ++ |
| if(u != root || flag > 1) |
| cut[u] = true |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]) |
| } |
| } |
| } |
| } |
| void work() |
| { |
| for (int i = 1 |
| { |
| int u = read(), v = read() |
| Insert(u, v), Insert(v, u) |
| } |
| for (int i = 1 |
| { |
| if(! dfn[i]) |
| { |
| root = i |
| Tarjan(i, 0) |
| } |
| } |
| return |
| } |
| }T_cut |
| struct Tarjan_v_DCC //无向图 点双联通分量 |
| { |
| struct Edge |
| { |
| int to, next |
| }e[maxn << 1], g[maxn << 1] |
| int head[maxn], tot = 1 |
| int headg[maxn], totg = 1 |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot) |
| } |
| void Insertg(int u, int v) |
| { |
| return (void)(g[++ totg].to = v, g[totg].next = headg[u], headg[u] = totg) |
| } |
| int dfn[maxn], low[maxn], Time |
| int stk[maxn], top |
| int root |
| bool cut[maxn] |
| int num, new_id[maxn], bel[maxn] |
| vector <int> dcc[maxn] |
| int tot_dcc |
| |
| void Tarjan(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time |
| stk[++ top] = u |
| if(u == root && head[u] == 0) |
| { |
| dcc[++ tot_dcc].push_back(u) |
| return |
| } |
| int flag = 0 |
| fort(u) |
| { |
| if(v == fa) continue |
| if(! dfn[v]) |
| { |
| Tarjan(v, u) |
| low[u] = min(low[u], low[v]) |
| if(low[v] >= dfn[u]) |
| {//考Tarjan e_DCC 的比赛 |
| flag ++ |
| if(u != root || flag > 1) cut[u] = true |
| tot_dcc ++ |
| while(stk[top + 1] != v) |
| { |
| dcc[tot_dcc].push_back(stk[top --]) |
| } |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]) |
| } |
| } |
| } |
| return |
| } |
| void SD_v_DCC() |
| { |
| num = tot_dcc |
| for (int i = 1 |
| { |
| if(cut[i]) |
| { |
| new_id[i] = ++ num |
| } |
| } |
| for (int i = 1 |
| { |
| for (int j = 0 |
| { |
| int x = dcc[i][j] |
| if(cut[x]) |
| { |
| Insertg(i, new_id[x]), Insertg(new_id[x], i) |
| } |
| else |
| { |
| bel[x] = i |
| } |
| } |
| } |
| return |
| } |
| void work() |
| { |
| for (int i = 1 |
| { |
| int u = read(), v = read() |
| Insert(u, v), Insert(v, u) |
| } |
| for (int i = 1 |
| { |
| if(! dfn[i]) |
| { |
| Tarjan(i, 0) |
| } |
| } |
| SD_v_DCC() |
| return |
| } |
| }T_v_DCC |
| struct Tarjan_e_DCC //无向图 边双联通分量 |
| { |
| struct Edge |
| { |
| int to, next |
| }e[maxn << 1], g[maxn << 1] |
| int head[maxn], tot = 1 |
| int headg[maxn], totg = 1 |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot) |
| } |
| void Insertg(int u, int v) |
| { |
| return (void)(g[++ totg].to = v, g[totg].next = headg[u], headg[u] = totg) |
| } |
| int dfn[maxn], low[maxn], Time |
| int bel[maxn], tot_dcc |
| bool bridge[maxn] |
| void Tarjan_bridge(int u, int fa) |
| { |
| dfn[u] = low[u] = ++ Time |
| fort(u) |
| { |
| if(v == fa) continue |
| if(!dfn[v]) |
| { |
| Tarjan_bridge(v, u) |
| low[u] = min(low[u], low[v]) |
| if(low[v] > dfn[u]) |
| bridge[i]= bridge[i ^ 1] = true |
| } |
| else |
| { |
| low[u] = min(low[u], dfn[v]) |
| } |
| } |
| return |
| } |
| void Dfs(int u) |
| { |
| bel[u] = tot_dcc |
| fort(u) |
| { |
| if(bel[v] || bridge[i]) continue |
| Dfs(v) |
| } |
| return |
| } |
| void SD_e_DCC() |
| { |
| for (int i = 2 |
| { |
| int u = e[i ^ 1].to, v = e[i].to |
| if(bel[u] == bel[v]) continue |
| Insertg(bel[u], bel[v]) |
| } |
| return |
| } |
| void work() |
| { |
| for (int i = 1 |
| { |
| int u = read(), v = read() |
| Insert(u, v), Insert(v, u) |
| } |
| for (int i = 1 |
| { |
| if(!dfn[i]) |
| { |
| Tarjan_bridge(i, 0) |
| } |
| } |
| for (int i = 1 |
| { |
| if(! bel[i]) |
| { |
| ++ tot_dcc, Dfs(i) |
| } |
| } |
| SD_e_DCC() |
| return |
| } |
| }T_e_DCC |
| struct Tarjan_SCC //有向图 强连通分量 |
| { |
| struct Edge |
| { |
| int to, next |
| }e[maxn << 1], g[maxn << 1] |
| int head[maxn], tot = 1 |
| int headg[maxn], totg = 1 |
| void Insert(int u, int v) |
| { |
| return (void)(e[++ tot].to = v, e[tot].next = head[u], head[u] = tot) |
| } |
| void Insertg(int u, int v) |
| { |
| return (void)(g[++ totg].to = v, g[totg].next = headg[u], headg[u] = totg) |
| } |
| int dfn[maxn], low[maxn], Time |
| int stk[maxn], top |
| bool inv[maxn] |
| int tot_scc, bel[maxn] |
| vector <int> scc[maxn] |
| |
| void Tarjan(int u) |
| { |
| dfn[u] = low[u] = ++ Time |
| stk[++ top] = u, inv[u] = true |
| fort(u) |
| { |
| if(!dfn[v]) |
| { |
| Tarjan(v) |
| low[u] = min(low[u], low[v]) |
| } |
| else if(inv[v]) |
| { |
| low[u] = min(low[u], dfn[v]) |
| } |
| } |
| if(dfn[u] == low[u]) |
| { |
| ++ tot_scc |
| while(stk[top + 1] != u) |
| { |
| int v = stk[top] |
| inv[v] = false |
| bel[v] = tot_scc |
| scc[tot_scc].push_back(v) |
| } |
| } |
| return |
| } |
| void SD_SCC() |
| { |
| for (int u = 1 |
| { |
| fort (u) |
| { |
| if(bel[u] == bel[v]) continue |
| Insert(bel[u], bel[v]) |
| } |
| } |
| return |
| } |
| void work() |
| { |
| for (int i = 1 |
| { |
| int u = read(), v = read() |
| Insert(u, v) |
| } |
| for (int i = 1 |
| { |
| if(! dfn[i]) |
| { |
| Tarjan(i) |
| } |
| } |
| SD_SCC() |
| return |
| } |
| }T_SCC |
| void Solve() |
| { |
| return |
| } |
| // #undef int |
| int main() |
| { |
| Solve() |
| return 0 |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】