題目鏈接:P2746 [USACO5.3]校園網Network of Schools
用tarjan算法求出強連通分量,並且縮點,如果縮點後只有一個點,則答案爲1,0
對於第一問,如果縮點後某一點的入度爲0,則它肯定要一份軟件,如果不爲0,則一定可以從別的點獲得軟件,而對於下一個問題,縮點後,對每個出度爲0的點,需拓展一條出邊,對每個入度爲0的點,需拓展一條入邊,則可以完成條件。所以爲了拓展的邊更少,可以從出度爲0的點向入度爲0的點拓展邊。所以所需最少的拓展數即出度爲0的點數和入度爲0的點數的較大者。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<queue>
//#define ls (p<<1)
//#define rs (p<<1|1)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
//#define lowbit(p) p&(-p)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const ll INF = 1e18;
const int N = 5e5+7;
const int M = 5e5+7;
int head[N],nex[M],ver[M],tot = 1;
//vector<int>scc[N];
int cnt,n,m;
int dfn[N],low[N],num;
int stk[N],top;
int vis[N],c[N];
int in[N],out[N],ansi,anso;
int ins[N];//判斷是否在棧中
inline void read(int &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
void add(int x,int y){
ver[++tot] = y;nex[tot] = head[x];head[x] = tot;
}
void tarjan(int x){
dfn[x] = low[x] = ++num;
stk[++top] = x,ins[x] = 1;
for(int i = head[x];i;i = nex[i]){
int y = ver[i];
if(!dfn[y]){
tarjan(y);
low[x] = min(low[x],low[y]);
}
else if(ins[y])
low[x] = min(low[x],dfn[y]);
}
if(dfn[x] == low[x]){
cnt++;
int y;
do{
y = stk[top--];
ins[y] = 0;//已經出棧
c[y] = cnt;
//scc[cnt].push_back(y);
}while(x != y);
}
}
int x;
int main(){
cin>>n;
over(i,1,n){
scanf("%d",&x);
while(x != 0){
add(i,x);
scanf("%d",&x);
}
}
over(i,1,n)
if(!dfn[i])
tarjan(i);
over(i,1,n)
for(int j = head[i];j;j = nex[j]){
int k = ver[j];
if(c[k] != c[i]){
in[c[k]]++;//已經縮過點了,不能用原來的那一套了
out[c[i]]++;
}
}
over(i,1,cnt){//已經縮過點了,要看cnt
if(!in[i])ansi++;
if(!out[i])anso++;
}
if(cnt == 1){cout<<1<<endl<<0;return 0;}
cout<<ansi<<endl;
cout<<max(anso,ansi);
return 0;
}