P2746 校园网
题面:https://www.luogu.org/problemnew/show/P2746
对于子任务A,求需要给多少个学校发软件,即为求有多少个入度为0的点 因为对于对于每个入度不为0的点一定可以从一个其他点走到。
对于子任务B,求入度为0的点个数与出度为0的点个数的最大值,需要添多少条边才能形成使所有点都可以被某个点到达,所以对于每个出度为0的点,需要拓展一条出边,对于每个入度为0的点,需要拓展一条入边,为了减少拓展边数,可以将所有出度为0的点拓展出边到某个入度为0的点的上,最后答案即为 max。
Code:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn = 105;
int T,dfn[maxn],low[maxn],id[maxn],scc,vis[maxn];
int head[maxn],num_edge,n;
int rd[maxn],cd[maxn];
struct node{
int next;
int to;
}edge[maxn*maxn];
stack<int> st;
void add_edge(int from,int to){
num_edge++;
edge[num_edge].next=head[from];
edge[num_edge].to=to;
head[from]=num_edge;
}
void tarjan(int s){
dfn[s]=low[s]=++T;
vis[s]=true;
st.push(s);
for(int i=head[s];i!=0;i=edge[i].next){
int to=edge[i].to;
if(!dfn[to]){
tarjan(to);
low[s]=min(low[s],low[to]);
}else if(vis[to]){
low[s]=min(low[s],dfn[to]);
}
}
if(low[s]==dfn[s]){
scc++;
int u;
do{
u=st.top();
vis[u]=false;
st.pop();
id[u]=scc;
}while(u!=s);
}
return;
}
int main(){
cin>>n;
int a;
for(int i=1;i<=n;i++){
for(;;){
cin>>a;
if(a==0)break;
add_edge(i,a);
}
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
memset(vis,false,sizeof vis);
for(int i=1;i<=n;i++){
for(int k=head[i];k!=0;k=edge[k].next){
if(id[i]!=id[edge[k].to]){
vis[id[i]]=true;
cd[id[i]]++;
rd[id[edge[k].to]]++;
}
}
}
int ans1=0;
int ans2=0;
for(int i=1;i<=scc;i++){
if(rd[i]==0)ans1++;
if(cd[i]==0)ans2++;
}
cout<<ans1<<endl;
if(scc==1)cout<<0<<endl;
else cout<<max(ans1,ans2)<<endl;
}