题目地址:
https://www.luogu.com.cn/problem/P2863
有一个 n n n个点, m m m条边的有向图,请求出这个图点数大于 1 1 1的强联通分量个数。
输入格式:
第一行为两个整数
n
n
n和
m
m
m。
第二行至
m
+
1
m+1
m+1行,每一行有两个整数
a
a
a和
b
b
b,表示有一条从
a
a
a到
b
b
b的有向边。
输出格式:
仅一行,表示点数大于
1
1
1的强联通分量个数。
数据范围:
对于全部的测试点,保证
2
≤
n
≤
1
0
4
2\le n \le 10^4
2≤n≤104,
2
≤
m
≤
5
×
1
0
4
2\le m\le 5\times 10^4
2≤m≤5×104,
1
≤
a
,
b
≤
n
1 \leq a, b \leq n
1≤a,b≤n。
可以用Tarjan算法。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e4 + 10, M = 5e4 + 10;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, sz[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp;
stk[top++] = u;
in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in_stk[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
scc_cnt++;
int y;
do {
y = stk[--top];
in_stk[y] = false;
id[y] = scc_cnt;
sz[scc_cnt]++;
} while (y != u);
}
}
int main() {
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
int res = 0;
for (int i = 1; i <= scc_cnt; i++)
if (sz[i] > 1) res++;
printf("%d\n", res);
}
时间复杂度 O ( n + m ) O(n+m) O(n+m),空间 O ( n ) O(n) O(n)。