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;

}
posted @ 2019-07-16 12:44  prestige  阅读(143)  评论(0编辑  收藏  举报