1. 程式人生 > >2018.09.27 網路協議(tarjan)

2018.09.27 網路協議(tarjan)

描述

一些學校連線在一個計算機網路上。學校之間存在軟體支援協議。每個學校都有它應支援的學校名單(學校 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; }