2018.09.27 網路協議(tarjan)
阿新 • • 發佈:2018-12-12
描述
一些學校連線在一個計算機網路上。學校之間存在軟體支援協議。每個學校都有它應支援的學校名單(學校 a 支援學校 b ,並不表示學校 b 一定支援學校 a )。當某校獲得一個新軟體時,無論是直接得到還是網路得到,該校都應立即將這個軟體通過網路傳送給它應支援的學校。因此,一個新軟體若想讓所有連線在網路上的學校都能使用,只需將其提供給一些學校即可。 任務 請編一個程式,根據學校間支援協議(各個學校的支援名單),計算最少需要將一個新軟體直接提供給多少個學校,才能使軟體通過網路被傳送到所有學校; 如果允許在原有支援協議上新增新的支援關係。則總可以形成一個新的協議,使得此時只需將一個新軟體提供給任何一個學校,其他所有學校就都可以通過網路獲得該軟體。程式設計計算最少需要新增幾條新的支援關係。
輸入
第一行是一個正整數 n,表示與網路連線的學校總數。 隨後 n 行分別表示每個學校要支援的學校,即:i+1 行表示第ii號學校要支援的所有學校代號,最後 0 結束。 如果一個學校不支援任何其他學校,相應行則會有一個 0。一行中若有多個數字,數字之間以一個空格分隔。
輸出
包含兩行,第一行是一個正整數,表示任務 a 的解,第二行也是一個正整數,表示任務 b 的解。
樣例輸入
5 2 4 3 0 4 5 0 0 0 1 0
樣例輸出
1 2
提示
2≤n≤100。
標籤
IOI1996
就是一個tarjan求強連通分量的板題。 求出來之後根據連通分量的數量維護答案就行了。 程式碼:
#include<bits/stdc++.h>
#define N 50005
using namespace std;
struct Node{int v,next;}e[N<<2];
int first[N],dfn[N],low[N],ru[N],sig=0,chu[N],col[N],cnt=0,tot=0,n;
stack<int>S;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48 ),ch=getchar();
return ans;
}
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;}
void tarjan(int p){
dfn[p]=low[p]=++tot,S.push(p);
for(int i=first[p];i;i=e[i].next){
int v=e[i].v;
if(!dfn[v]){
tarjan(v);
low[p]=min(low[p],low[v]);
}
else if(!col[v])low[p]=min(low[p],dfn[v]);
}
if(dfn[p]==low[p]){
++sig;
int x;
do x=S.top(),S.pop(),col[x]=sig;while(x!=p);
}
}
int main(){
n=read();
for(int i=1;i<=n;++i){
int tmp;
while(tmp=read(),tmp)add(i,tmp);
}
for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
for(int i=1;i<=n;++i)
for(int j=first[i];j;j=e[j].next){
int v=e[j].v;
if(col[i]!=col[v])++ru[col[v]],++chu[col[i]];
}
int ans1=0,ans2=0;
for(int i=1;i<=sig;++i)ans1+=!ru[i],ans2+=!chu[i];
if(sig==1)printf("1\n0");
else printf("%d\n%d",ans1,max(ans1,ans2));
return 0;
}