题目模型:
最小放东西在几个点能传递到所有点。
最少加几条边就能放一个点传递到每个点。
转换模型:
强连通缩点
读为入度为0的点的数量。
max(入度为0的点的数量,出度为0的点的数量)。
原因: 因为没加一条边最多让某个点入度+1,某个点出度+1,而实现一个点传递到每个点就是要让每个点都有入度和出度。所以要取个max。
只有一个强连通的情况特判。
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <limits.h>
#include <string>
#include <iostream>
#include <queue>
#include <math.h>
#include <map>
#include <stack>
#include <sstream>
#include <set>
#include <iterator>
#include <list>
#include <cstdio>
#include <iomanip>
#include <climits>
using namespace std;
const int maxn = 200+100; //点数
int n,indeg[maxn],outdeg[maxn];
int scc, top, tot;
vector<int> G[maxn];
int low[maxn], dfn[maxn], belong[maxn];
int stk[maxn], vis[maxn];
void init(int n) {
for (int i = 0; i <= n; ++i) {
G[i].clear();
low[i] = 0;
dfn[i] = 0;
stk[i] = 0;
vis[i] = 0;
}
scc = top = tot = 0;
}
void tarjan(int x) {
stk[top++] = x;
low[x] = dfn[x] = ++tot;
vis[x] = 1;
for (int i=0; i<int(G[x].size()); ++i) {
int to=G[x][i];
if (!dfn[to]) {
tarjan(to);
low[x] = min(low[x], low[to]);
} else if (vis[to]) {
low[x] = min(low[x], dfn[to]);
}
}
if (low[x] == dfn[x]) {
++scc;
int temp;
do {
temp = stk[--top];
belong[temp] = scc;
vis[temp] = 0;
} while (temp != x);
}
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);cout.precision(10);cout << fixed;
#ifdef LOCAL_DEFINE
freopen("input.txt", "r", stdin);
#endif
scanf("%d", &n);
int x;
for(int i=1; i<=n; ++i){
while(true){
scanf("%d", &x);
if(x==0) break;
G[i].push_back(x);
}
}
for(int i=1; i<=n; ++i) if(!dfn[i]) tarjan(i);
memset(indeg, 0, sizeof(indeg));
for(int i=1; i<=n; ++i){
for(int k=0; k<int(G[i].size()); ++k){
int j=G[i][k];
if(belong[i]==belong[j]) continue;
++indeg[belong[j]];
++outdeg[belong[i]];
}
}
if(scc==1) {
puts("1");
puts("0");
}
else{
int in0=0, out0=0;
for(int i=1; i<=scc; ++i){
if(indeg[i]==0) ++in0;
if(outdeg[i]==0) ++out0;
}
cout<<in0<<'\n';
cout<<max(in0, out0)<<'\n';
}
#ifdef LOCAL_DEFINE
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
return 0;
}