P2812 校园网络【[USACO]Network of Schools加强版】(缩点)
P2812 校园网络【[USACO]Network of Schools加强版】
题目背景
浙江省的几所 OI 强校的神犇发明了一种人工智能,可以 AC 任何题目,所以他们决定建立一个网络来共享这个软件。但是由于他们脑力劳动过多导致全身无力身体被♂掏♂空,他们来找你帮助他们。
题目描述
共有 \(n\) 所学校 \((1 \leq n \leq 10000)\) 已知他们实现设计好的网络共 \(m\) 条线路,为了保证高速,网络是单向的。现在请你告诉他们至少选几所学校作为共享软件的母机,能使每所学校都可以用上。再告诉他们至少要添加几条线路能使任意一所学校作为母机都可以使别的学校使用上软件。
输入格式
第一行一个正整数 \(n\)。
接下来 \(n\) 行每行有若干个整数,用空格隔开。
第 \(i+1\) 行,每行输入若干个非零整数 \(x\),表示从 \(i\) 到 \(x\) 有一条线路。以 \(0\) 作为结束标志。
输出格式
第一行一个整数,表示至少选几所学校作为共享软件的母机,能使每所学校都可以用上。
第二行一个整数,表示至少要添加几条线路能使任意一所学校作为母机都可以使别的学校使用上软件。
输入输出样例 #1
输入 #1
5
2 0
4 0
5 0
1 0
0
输出 #1
2
2
说明/提示
POJ 原题。数据扩大了 \(100\) 倍。
$1 \leq $ 边数 \(\leq 5000000\),\(1 \leq n \leq 10000\) 。
实际上,\(1 \leq n \leq 10000\),$1\le $ 边数 \(\le 50000\)。
这是一道缩点的题,首先要记录每个点所在的强连通量,比如a可以到b,不可以到a,这时a和b是属于两个连通量,然后遍历每个点,如果一个点和他的子节点在一个强连通分量当中,直接跳过,如果不同,说明父节点有一条指向子节点的边,父节点的出度++,子节点的入度++,最后需要安装的电话的数目就是入度为零的点,首先强连通分量的点很好理解,他们相互连通,入度为零,只需要一个电话,然后其他入度为零的点说明它可以到其他点,只需要一个电话,如果入度不为零,说明它可以由其他点把电话给他。
忘了说,我灵机一动把else if(vis[y])直接改成else,这个会导致错误,因为有可能y点已经出栈了
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
#define int long long
const int N=1e5+5;
int n,m,a=0,b=0;
vector<int>v[N];
int low[N],dfsn[N],vis[N],scc[N],inc[N],outc[N];
int t=0,cnt=0;
stack<int>s;
void dfs(int x){
low[x]=dfsn[x]=++t;
s.push(x),vis[x]=1;
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(dfsn[x]==low[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>>m;
while(m!=0){
v[i].push_back(m);
cin>>m;
}
}
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]){
outc[scc[x]]++;
inc[scc[y]]++;
}
}
}
for(int i=1;i<=cnt;i++){
if(!outc[i])a++;
if(!inc[i])b++;
}
cout<<b<<endl;
if(cnt==1)cout<<0;
else cout<<max(a,b);
return 0;
}

浙公网安备 33010602011771号