P2746 [USACO5.3] 校园网Network of Schools(缩点)

P2746 [USACO5.3] 校园网Network of Schools

题目描述

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 \(B\)\(A\) 学校的分发列表中,\(A\) 也不一定在 \(B\) 学校的列表中。

你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

输入格式

输入文件的第一行包括一个正整数 \(N\),表示网络中的学校数目。学校用前 \(N\) 个正整数标识。

接下来 \(N\) 行中每行都表示一个接收学校列表(分发列表),第 \(i+1\) 行包括学校 \(i\) 的接收学校的标识符。每个列表用 \(0\) 结束,空列表只用一个 \(0\) 表示。

输出格式

你的程序应该在输出文件中输出两行。

第一行应该包括一个正整数,表示子任务 A 的解。

第二行应该包括一个非负整数,表示子任务 B 的解。

输入输出样例 #1

输入 #1

5
2 4 3 0
4 5 0
0
0
1 0

输出 #1

1
2

说明/提示

\(2 \le N \le 100\)

题目翻译来自NOCOW。

USACO Training Section 5.3
这道题也是一道缩点题,第一问很简单,求入度为零的点就行,第二个问求入度为零的点和出度为零的点的最大值,但要特判,所有学校都在一个连通图中,我们原先的做法会输出1,因为强连通分量的入度和出度都为零,这时候我们要特判一下,如果一个连通分量的出度和入度都为零,这就是一个强连通分量,如果入度和出度为零的点都为一,并且强连通分量为一,这说明出度和入度为零都是这个强连通分量提供的,这时我们不需要额外建边,直接输出零就行

#include<iostream>
#include<vector>
#include<stack>
#define int long long
using namespace std;
const int N=1e5+5;
int n,a,b,t=0,cnt=0;
int low[N],dfsn[N],scc[N],vis[N],inc[N],outc[N];
vector<int>v[N];
stack<int>s;
int ans1=0,ans2=0;
void dfs(int x){
    low[x]=dfsn[x]=++t;
    vis[x]=1,s.push(x);
    for(int y:v[x]){
        if(!dfsn[y]){
            dfs(y);
            low[x]=min(low[x],low[y]);
        }
        else if(vis[y])low[x]=min(low[x],dfsn[y]);
    }
    if(low[x]==dfsn[x]){
        ++cnt;
        while(s.top()!=x){
            vis[s.top()]=0;
            scc[s.top()]=cnt;
            s.pop();
        }
        vis[s.top()]=0;
        scc[s.top()]=cnt;
        s.pop();
    }
}
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>b;
        while(b!=0){
            v[i].push_back(b);
            cin>>b;
        }
    }
    for(int i=1;i<=n;i++)if(!dfsn[i])dfs(i);
    for(int x=1;x<=n;x++){
        for(int y:v[x]){
            if(scc[x]!=scc[y]){
                inc[scc[y]]++;
                outc[scc[x]]++;
            }
        }
    }
    int num=0;
    for(int i=1;i<=cnt;i++){
        if(!inc[i]&&!outc[i])num++;
        if(!inc[i])ans1++;
        if(!outc[i])ans2++;
    }
    cout<<ans1<<endl;
    if(ans1==1&&ans2==1&&num==1)cout<<0;
    else cout<<max(ans1,ans2);
    return 0;
}
posted @ 2025-03-16 22:03  郭轩均  阅读(27)  评论(0)    收藏  举报