校园网络
题目背景
浙江省的几所OI强校的神犇发明了一种人工智能,可以AC任何题目,所以他们决定建立一个网络来共享这个软件。但是由于他们脑力劳动过多导致全身无力身体被♂掏♂空,他们来找你帮助他们。
题目描述
共有n所学校(n<=10000)已知他们实现设计好的网络共m条线路,为了保证高速,网络是单向的。现在请你告诉他们至少选几所学校作为共享软件的母机母鸡,能使每所学校都可以用上。再告诉他们至少要添加几条线路能使任意一所学校作为母机母鸡都可以使别的学校使用上软件。
输入输出格式
输入格式:
第一行一个整数n。
接下来n行每行有若干个整数,用空格空格隔开。
第i-1行的非零整数x,表示从i到x有一条线路。以0作为结束标志。
输出格式:
第一行一个整数表示问题1的答案。
第二行回答问题2.
输入输出样例
说明
POJ原题。数据扩大了100倍。
边数 $leq 5000000$
分析:
$Tarjan$裸题。
因为在一个强连通分量中的电脑可以相互传递信息,所以可以缩点。所点以后在重建边,此时整张图可能是一个非连通图,那么我们就查询有哪些点入度为$0$,这些点就是母机(母鸡)。然后我们再找入度为$0$的点和出度为$0$的点,取$max$就可以得到第二问的答案。因为只要把出度为$0$的点与入度为$0$的点依次连接就可以形成一个大环,这样就能保证所有计算机都能相互到达了。(不过要特判只有一个强连通分量的情况,只有一个强连通分量的话就不需要再连边,第二问答案也就是$0$)
Code:
//It is made by HolseLee on 20th Aug 2018 //Luogu.org P2812 #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<iomanip> #include<algorithm> #include<vector> #include<stack> #define Max(a,b) (a)>(b)?(a):(b) #define Min(a,b) (a)<(b)?(a):(b) using namespace std; const int N=1e4+7; int n,dfn[N],low[N],belong[N],scc,ans,idx,ru[N],chu[N]; bool vis[N]; vector<int>e[N]; stack<int>sta; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } void tarjan(int u) { dfn[u]=low[u]=++idx; vis[u]=true;sta.push(u); int v; for(int i=0;i<e[u].size();++i){ v=e[u][i]; if(!dfn[v]){ tarjan(v); low[u]=Min(low[u],low[v]); }else if(vis[v]){ low[u]=Min(low[u],dfn[v]); } } if(dfn[u]==low[u]){ scc++; do{ v=sta.top();sta.pop(); vis[v]=false;belong[v]=scc; }while(v!=u); } } int main() { n=read(); int x; for(int i=1;i<=n;++i){ x=read(); while(x){ e[i].push_back(x); x=read(); } } for(int i=1;i<=n;++i) if(!dfn[i])tarjan(i); for(int i=1;i<=n;++i) for(int j=0;j<e[i].size();++j){ if(belong[i]!=belong[e[i][j]]) ++chu[belong[i]],++ru[belong[e[i][j]]]; } for(int i=1;i<=scc;++i) if(!ru[i])++ans; printf("%d ",ans); int tot=0; for(int i=1;i<=scc;++i) if(!chu[i])++tot; ans=Max(ans,tot); if(scc==1)ans=0; printf("%d ",ans); return 0; }