目录
- [1 介绍](#1 介绍)
- [2 训练](#2 训练)
1 介绍
本博客介绍有向图的强连通分量的题目。
连通分量:是针对有向图的一个概念。对于分量中任意两个结点a、b,必然可以从a走到b,且从b走到a。
强连通分量:是针对有向图的一个概念。极大强连通分量,也就是说再加一个结点,它就不是连通分量。
强连通分量,用来将一个有向图转化为一个有向无环图(DAG、拓扑图)。方法是缩点,将所有连通分量缩成一个点。
有向无环图有很多好处,可以递推(即拓扑序)求最短路或最长路。
求解方法:Tarjan算法。
2 训练
题目1 :1174受欢迎的牛
C++代码如下,
cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010, M = 50010;
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, Size[N];
int dout[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 != -1; i = ne[i]) {
int j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min(low[u], low[j]);
} else if (in_stk[j]) {
low[u] = min(low[u], dfn[j]);
}
}
if (dfn[u] == low[u]) {
++scc_cnt;
int y;
do {
y = stk[top--];
in_stk[y] = false;
id[y] = scc_cnt;
Size[scc_cnt] ++;
} while (y != u);
}
}
int main() {
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
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);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = h[i]; ~j; j = ne[j]) {
int k = e[j];
int a = id[i], b = id[k];
if (a != b) dout[a]++;
}
}
int zeros = 0, sum = 0;
for (int i = 1; i <= scc_cnt; ++i) {
if (!dout[i]) {
zeros++;
sum += Size[i];
if (zeros > 1) {
sum = 0;
break;
}
}
}
printf("%d\n", sum);
return 0;
}
题目2 :367学校网络
C++代码如下,
cpp
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110, M = 10010;
int n;
int h[N], e[M], ne[M], idx;
int dfn[M], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt;
int din[N], dout[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 j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min(low[u], low[j]);
} else if (in_stk[j]) {
low[u] = min(low[u], dfn[j]);
}
}
if (dfn[u] == low[u]) {
++scc_cnt;
int y;
do {
y = stk[top--];
in_stk[y] = false;
id[y] = scc_cnt;
} while (y != u);
}
}
int main() {
cin >> n;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; ++i) {
int t;
while (cin >> t, t) add(i, t);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) {
tarjan(i);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = h[i]; j != -1; j = ne[j]) {
int k = e[j];
int a = id[i], b = id[k];
if (a != b) {
dout[a]++;
din[b]++;
}
}
}
int a = 0, b = 0;
for (int i = 1; i <= scc_cnt; ++i) {
if (!din[i]) a++;
if (!dout[i]) b++;
}
printf("%d\n", a);
if (scc_cnt == 1) puts("0");
else printf("%d\n", max(a, b));
return 0;
}
题目3 :1175最大半连通子图
C++代码如下,
cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_set>
using namespace std;
typedef long long LL;
const int N = 100010, M = 2000010;
int n, m, mod;
int h[N], hs[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, scc_size[N];
int f[N], g[N];
void add(int h[], 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 j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min(low[u], low[j]);
} else if (in_stk[j]) {
low[u] = min(low[u], dfn[j]);
}
}
if (dfn[u] == low[u]) {
++scc_cnt;
int y;
do {
y = stk[top--];
in_stk[y] = false;
id[y] = scc_cnt;
scc_size[scc_cnt]++;
} while (y != u);
}
}
int main() {
memset(h, -1, sizeof h);
memset(hs, -1, sizeof hs);
scanf("%d%d%d", &n, &m, &mod);
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
add(h, a, b);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) {
tarjan(i);
}
}
unordered_set<LL> S;
for (int i = 1; i <= n; ++i) {
for (int j = h[i]; ~j; j = ne[j]) {
int k = e[j];
int a = id[i], b = id[k];
LL hash = a * 1000000ll + b;
if (a != b && !S.count(hash)) {
add(hs, a, b);
S.insert(hash);
}
}
}
for (int i = scc_cnt; i; i--) {
if (!f[i]) {
f[i] = scc_size[i];
g[i] = 1;
}
for (int j = hs[i]; ~j; j = ne[j]) {
int k = e[j];
if (f[k] < f[i] + scc_size[k]) {
f[k] = f[i] + scc_size[k];
g[k] = g[i];
} else if (f[k] == f[i] + scc_size[k]) {
g[k] = (g[k] + g[i]) % mod;
}
}
}
int maxf = 0, sum = 0;
for (int i = 1; i <= scc_cnt; ++i) {
if (f[i] > maxf) {
maxf = f[i];
sum = g[i];
} else if (f[i] == maxf) sum = (sum + g[i]) % mod;
}
printf("%d\n", maxf);
printf("%d\n", sum);
return 0;
}
题目4 :368银河
C++代码如下,
cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, M = 600010;
int n, m;
int h[N], hs[N], e[M], ne[M], w[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, sz[N];
int dist[N];
void add(int h[], int a, int b, int c) {
e[idx] = b, w[idx] = c, 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 j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min(low[u], low[j]);
} else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
}
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() {
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
memset(hs, -1, sizeof hs);
for (int i = 1; i <= n; ++i) add(h, 0, i, 1);
while (m--) {
int t, a, b;
scanf("%d%d%d", &t, &a, &b);
if (t == 1) add(h, b, a, 0), add(h, a, b, 0);
else if (t == 2) add(h, a, b, 1);
else if (t == 3) add(h, b, a, 0);
else if (t == 4) add(h, b, a, 1);
else add(h, a, b, 0);
}
tarjan(0);
bool success = true;
for (int i = 0; i <= n; ++i) {
for (int j = h[i]; ~j; j = ne[j]) {
int k = e[j];
int a = id[i], b = id[k];
if (a == b) {
if (w[j] > 0) {
success = false;
break;
}
} else {
add(hs, a, b, w[j]);
}
}
if (!success) break;
}
if (!success) puts("-1");
else {
for (int i = scc_cnt; i; --i) {
for (int j = hs[i]; ~j; j = ne[j]) {
int k = e[j];
dist[k] = max(dist[k], dist[i] + w[j]);
}
}
LL res = 0;
for (int i = 1; i <= scc_cnt; ++i) {
res += (LL)dist[i] * sz[i];
}
printf("%lld\n", res);
}
return 0;
}