Tarjan
\(Tarjan\) 缩点
有向图缩点
struct SCC {
int n, m;
vector<int> head, from, to, nxt;
vector<int> weight;
int totEdge = 1;
void add(int x, int y, int z = 0) {
++totEdge;
weight[totEdge] = z;
from[totEdge] = x;
to[totEdge] = y;
nxt[totEdge] = head[x];
head[x] = totEdge;
}
SCC(int n, int m) : n(n), m(m) {
head.assign(n + 5, 0);
from.assign(m * 2 + 5, 0);
to.assign(m * 2 + 5, 0);
nxt.assign(m * 2 + 5, 0);
weight.assign(m * 2 + 5, 0);
init();
}
vector<int> dfn, low;
vector<int> stk, instk;
vector<int> scc, size;
int tot; // count of dfn
int cnt; // count of scc
void tarjan(int u) {
dfn[u] = low[u] = ++tot;
stk.push_back(u);
instk[u] = 1;
for (int e = head[u]; e; e = nxt[e]) {
int v = to[e];
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instk[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
int v;
++cnt;
do {
v = stk.back();
stk.pop_back();
instk[v] = 0;
scc[v] = cnt;
size[cnt]++;
} while (u != v);
}
}
// extension function
int operator[](int idx) {
return this->scc[idx];
}
void init() {
totEdge = 1;
for (int i = 1; i <= n; i++) head[i] = 0;
}
void work() {
dfn.assign(n + 1, 0);
low.assign(n + 1, 0);
instk.assign(n + 1, 0);
scc.assign(n + 1, 0);
size.assign(n + 1, 0);
tot = cnt = 0;
stk.reserve(n + 1);
for (int i = 1; i <= n; i++) {
if (!dfn[i]) tarjan(i);
}
}
};
使用方法(例题):
void solve() {
int n, m;
read(n, m);
// 初始化,n为点数,m为边数
SCC scc(n, m);
for (int i = 0, u, v; i < m; i++) {
read(u, v);
// 加边
scc.add(u, v);
}
// 获取强连通分量
scc.work();
// scc.cnt 强联通分量的个数
// scc.size[] 对应强连通分量的元素个数,下标为强连通分量的编号
// scc[] 获取某个点的强连通分量编号
int ans = 0;
for (int i = 1; i <= scc.cnt; i++) {
ans += scc.size[i] > 1;
}
cout << ans << '\n';
}
无向图缩点
struct Bridge {
int n, m;
vector<int> head, from, to, nxt;
vector<int> weight;
int totEdge = 1;
void add(int x, int y, int z = 0) {
++totEdge;
weight[totEdge] = z;
from[totEdge] = x;
to[totEdge] = y;
nxt[totEdge] = head[x];
head[x] = totEdge;
}
Bridge(int n, int m) : n(n), m(m) {
head.assign(n + 5, 0);
from.assign(m * 2 + 5, 0);
to.assign(m * 2 + 5, 0);
nxt.assign(m * 2 + 5, 0);
weight.assign(m * 2 + 5, 0);
init();
}
vector<int> dfn, low;
vector<bool> bridge;
vector<int> dcc, size;
int tot; // count of dfn
int cnt; // count of dcc
void tarjan(int u, int in_edge) {
dfn[u] = low[u] = ++tot;
for (int e = head[u]; e; e = nxt[e]) {
int v = to[e];
if (!dfn[v]) {
tarjan(v, e);
low[u] = min(low[u], low[v]);
if (low[v] > low[u]) {
bridge[e] = bridge[e ^ 1] = true;
}
}
else if (e != (in_edge ^ 1)) low[u] = min(low[u], dfn[v]);
}
}
void dfs(int u) {
dcc[u] = cnt;
++size[u];
for (int e = head[u]; e; e = nxt[e]) {
int v = to[e];
if (dcc[v] || bridge[e]) continue;
dfs(v);
}
}
bool operator[](int idx) const {
return this->dcc[idx];
}
void init() {
totEdge = 1;
for (int i = 1; i <= n; i++) head[i] = 0;
}
void work() {
dfn.assign(n + 1, 0);
low.assign(n + 1, 0);
bridge.assign(totEdge + 1, 0);
dcc.assign(n + 1, 0);
size.assign(n + 1, 0);
tot = cnt = 0;
for (int i = 1; i <= n; i++) {
if (!dfn[i]) tarjan(i, 0);
}
for (int i = 1; i <= n; i++) {
if (!dcc[i]) {
++cnt;
dfs(i);
}
}
}
};
使用方法 例题:
void solve() {
int n, m;
read(n, m);
// 初始化,n为点数,m为边数
Bridge scc(n, m);
for (int i = 0, u, v; i < m; i++) {
read(u, v);
// 加边
scc.add(u, v);
}
// 获取强连通分量
scc.work();
// scc.cnt 强联通分量的个数
// scc.size[] 对应强连通分量的元素个数,下标为强连通分量的编号
// scc[] 获取某个点的强连通分量编号
// scc.bridge[] 某个边是否为桥
int ans = 0;
for (int i = 1; i <= scc.cnt; i++) {
ans += scc.size[i] > 1;
}
cout << ans << '\n';
}

浙公网安备 33010602011771号