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;
}

浙公网安备 33010602011771号