题目
一些学校通过网络连接在一起,每个学校手中有一份名单,即它所指向的点。学校A的名单中有学校B,并不能保证学校B的名单里有学校A。
现在有一软件。
1.问至少发给几个学校才能保证所有的学校都可以得到该软件。
2.至少加几条边才能使将软件发给任何一个学校后,其他所有学校都可以得到软件。
N<=100000
题解
一个有向图,第一问的实质是询问入度为0的点有多少,第二问的可将所有出度/入度为0的点完全相连,因为强联通分量中任意的两点互达,所以需要先对图进行缩点操作。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
struct Edge {
int from, to, d;
Edge *Next;
}edge[10010], *First[10010];
int n, m;
int DFN[10010], LOW[10010];
bool instack[10010];
int s[10010], Stop;
int in_0, out_0;
int Dindex, Belong[10010], Bcnt;
int in_Edge[10010], out_Edge[10010];
void tarjan(int i) {
DFN[i] = LOW[i] = ++Dindex;
s[++Stop] = i;
instack[i] = true;
for(Edge *e = First[i]; e; e = e->Next) {
int j = e->to;
if(!DFN[j]) {
tarjan(j);
LOW[i] = min(LOW[i], LOW[j]);
} else if(instack[j]){
LOW[i] = min(LOW[i], DFN[j]);
}
}
if(DFN[i] == LOW[i]) {
Bcnt++; int j;
do {
j = s[Stop--];
Belong[j] = Bcnt;
instack[j] = false;
} while(j != i);
}
}
void solve() {
memset(instack, 0, sizeof(instack));
memset(DFN, 0, sizeof(DFN));
memset(LOW, 0, sizeof(LOW));
for(int i = 1; i <= n; i++) {
if(!DFN[i]) tarjan(i);
}
for(int i = 1; i <= n; i++)
for(Edge *e = First[i]; e; e = e->Next) {
if(Belong[e->to] != Belong[i]) {
out_Edge[Belong[i]]++;
in_Edge[Belong[e->to]]++;
}
}
for(int i = 1; i <= Bcnt; i++) {
if(!in_Edge[i]) in_0++;
if(!out_Edge[i]) out_0++;
}
}
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
edge[i].Next = First[u];
edge[i].from = u;
edge[i].to = v;
First[u] = &edge[i];
}
solve();
int ans = max(in_0, out_0);
cout << in_0 << endl << out_0;
return 0;
}