「洛谷 P2746」校园网 Network of Schools - 强连通分量

题意描述

洛谷链接

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 \(B\)\(A\) 学校的分发列表中,\(A\) 也不一定在 \(B\) 学校的列表中。

你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

解题思路

显然这道题考的是强连通分量。将学校用节点表示,分发关系用有向边表示,接着用 Tarjan 求一遍之后缩点即可。

第一问答案即为缩点后入度为 \(0\) 的节点个数,第二问答案即为入度为 \(0\) 和出度为 \(0\) 的节点数两者的最大值。

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <stack>

const int MAXN = 100;

int n;
std::vector<int> e[MAXN + 1];
int dfn[MAXN + 1], low[MAXN + 1], num = 0, cnt = 0;
std::stack<int> sta;
int ins[MAXN + 1], c[MAXN + 1];
std::vector<int> scc[MAXN + 1];
std::vector<int> ec[MAXN + 1], ecRev[MAXN + 1];

void tarjan(int x) {
dfn[x] = low[x] = ++num;
sta.push(x);
ins[x] = 1;
for (int each : e[x]) {
if (!dfn[each]) {
tarjan(each);
low[x] = std::min(low[x], low[each]);
} else if (ins[each]) {
low[x] = std::min(low[x], dfn[each]);
}
}
if (dfn[x] == low[x]) {
cnt++;
int y;
do {
y = sta.top();
sta.pop();
ins[y] = 0;
c[y] = cnt, scc[cnt].push_back(y);
} while (x != y);
}
}

int main() {
std::cin >> n;

for (int i = 1, x; i <= n; i++) {
while (scanf("%d", &x) != EOF) {
if (x == 0) break;
e[i].push_back(x);
}
}

memset(dfn, 0, sizeof(dfn));
for (int i = 1; i <= n; i++) {
if(!dfn[i]) {
tarjan(i);
}
}

for (int i = 1; i <= n; i++) {
for (int each : e[i]) {
if (c[i] == c[each]) continue;
ec[c[i]].push_back(c[each]);
ecRev[c[each]].push_back(c[i]);
}
}

int count1 = 0, count2 = 0;
for (int i = 1; i <= cnt; i++) {
if (ecRev[i].size() == 0) count1++;
if (ec[i].size() == 0) count2++;
}
count2 = std::max(count1, count2);
if (cnt == 1) count1 = 1, count2 = 0;

std::cout << count1 << std::endl << count2 << std::endl;

return 0;
}