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])
{//考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; 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; i; i = e[i].next, 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; char ch = getchar();
while(ch < '0' || ch > '9'){ if(ch == '-') w = 1; ch = getchar(); }
while(ch >= '0' && ch <= '9'){ x = (x << 1) + (x << 3) + (ch ^ '0'); ch = getchar(); }
if(w) return -x; 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; 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;
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;
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; 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;
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;
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;
void Solve()
{
return;
}
// #undef int
int main()
{
Solve();
return 0;
}