有向图。显然同一个SCC中的任意两个学校都可以共享到软件。那么Tarjan求强连通分量后缩点,统计所有入度为 0 的SCC,它们是必须被我们提供软件的。这是第一问。
第二问,题意即为最少添加几条有向边使整张图成为一个强连通图。统计零出度点。容易发现所有零入度和零出度都必须成为边的一端才能实现上述要求。则 max ( cnt 零入,cnt 零出 ) 即为答案。很显然啦。
要注意如果缩点之后图已是一张强连通图,那么要特判输出 0 。因为此时两个 cnt 均会为1。
有机会还是要背一背头文件,像这样乱堆一通很难受。…………
code:
#include<cmath>
#include<ios>
#include<fstream>
#include<istream>
#include<ostream>
#include <math.h>
#include <stdio.h>
#include <limits.h>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int maxn=101;
int n;
int Link[maxn];
struct edge{
int y,next;
}e[maxn*maxn];
int Tot;
int cnt;
int Struck[maxn];
int Top;
bool Ins[maxn];
int dfn[maxn];
int low[maxn];
int Num;
int Belong[maxn];
int In[maxn];
int Out[maxn];
inline void Read(int &x)
{
x=0;int f=1;char s=getchar();
for(;s<'0'||s>'9';s=getchar()) if(s=='-') f=-1;
for(;s>='0'&&s<='9';s=getchar()) x=(x<<3)+(x<<1)+s-48;
x*=f;
}
inline void Insert(int x,int y)
{
e[++Tot].y=y;
e[Tot].next=Link[x];Link[x]=Tot;
}
void Tarjan(int x)
{
dfn[x]=low[x]=++Num;
Struck[++Top]=x;Ins[x]=1;
for(int i=Link[x];i;i=e[i].next)
if(!dfn[e[i].y]) Tarjan(e[i].y),low[x]=min(low[x],low[e[i].y]);
else if(Ins[e[i].y]) low[x]=min(low[x],dfn[e[i].y]);
if(dfn[x]==low[x]){
++cnt;
while(Struck[Top]!=x) Belong[Struck[Top]]=cnt,Ins[Struck[Top]]=0,--Top;
Belong[x]=cnt;Ins[x]=0;--Top;
}
}
void Init()
{
Tot=0;
Read(n);
for(int i=1;i<=n;++i)
{
int x;Read(x);
while(x) Insert(i,x),Read(x);
}
}
void Work()
{
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(Ins,0,sizeof(Ins));
memset(Belong,0,sizeof(Belong));
Num=0;Top=0;cnt=0;
for(int i=1;i<=n;++i)
if(!dfn[i]) Tarjan(i);
memset(In,0,sizeof(In));
memset(Out,0,sizeof(Out));
for(int x=1;x<=n;++x)
for(int i=Link[x];i;i=e[i].next)
if(Belong[x]!=Belong[e[i].y]) ++Out[Belong[x]],++In[Belong[e[i].y]];
int In_zero=0,Out_zero=0;
for(int i=1;i<=cnt;++i)
{
if(!In[i]) ++In_zero;
if(!Out[i]) ++Out_zero;
}
printf("%d\n",In_zero);
if(cnt>1) printf("%d",max(In_zero,Out_zero));
else printf("0");
}
int main()
{
Init();
Work();
return 0;
}