1. 程式人生 > 其它 >#10093. 「一本通 3.5 練習 1」網路協議

#10093. 「一本通 3.5 練習 1」網路協議

技術標籤:強連通

一些學校連線在一個計算機網路上。學校之間存在軟體支援協議。每個學校都有它應支援的學校名單(學校 支援學校 ,並不表示學校 一定支援學校 )。當某校獲得一個新軟體時,無論是直接得到還是網路得到,該校都應立即將這個軟體通過網路傳送給它應支援的學校。因此,一個新軟體若想讓所有連線在網路上的學校都能使用,只需將其提供給一些學校即可。

任務
請編一個程式,根據學校間支援協議(各個學校的支援名單),計算最少需要將一個新軟體直接提供給多少個學校,才能使軟體通過網路被傳送到所有學校;
如果允許在原有支援協議上新增新的支援關係。則總可以形成一個新的協議,使得此時只需將一個新軟體提供給任何一個學校,其他所有學校就都可以通過網路獲得該軟體。程式設計計算最少需要新增幾條新的支援關係。

輸入格式
第一行是一個正整數 ,表示與網路連線的學校總數。 隨後 行分別表示每個學校要支援的學校,即: 行表示第 號學校要支援的所有學校代號,最後 結束。
如果一個學校不支援任何其他學校,相應行則會有一個 。一行中若有多個數字,數字之間以一個空格分隔。

輸出格式
包含兩行,第一行是一個正整數,表示任務 a 的解,第二行也是一個正整數,表示任務 b 的解。

樣例
輸入
5
2 4 3 0
4 5 0
0
0
1 0
輸出
1
2
資料範圍與提示
這道題和受歡迎的牛還是有點像
sc是強連通塊的塊數
scc表示不同的強連通塊,sz表示強連通塊中的點數
入度為零的點就是需要提供軟體的點,任務一的解就是入度為0的點,支援關係就是不管是入度還是出度,任務二的解是選擇出度和入度中較大的點數,如果不是較大的點數,那麼會出現仍然有點未被連結的情況。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stack>
#define mem(a,b) memset(a,b,sizeof(a))
const int N=202000;
typedef long long ll;
using namespace std;
struct node
{
    int to,net;
} e[N];
int head[N],tot;
void add(int u,int v)
{
    e[++tot].to=v;
    e[tot]
.net=head[u]; head[u]=tot; } int dfn[N],low[N],cnt,s[N],tp; int scc[N],sc; int sz[N],n,m,out[N],in[N]; void tarjan(int u) { low[u]=dfn[u]=++cnt; s[++tp]=u; for(int i=head[u];i;i=e[i].net) { int v=e[i].to; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(!scc[v]) low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { ++sc; while(s[tp]!=u) { scc[s[tp]]=sc; sz[sc]++; --tp; } scc[s[tp]]=sc,sz[sc]++,--tp; } } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { int u,v; while(~scanf("%d",&u)&&u) add(i,u); } for(int i=1; i<=n; i++) { if(!dfn[i]) tarjan(i); } for(int u=1; u<=n; u++) { for(int i=head[u]; i; i=e[i].net) { int v=e[i].to; if(scc[u]==scc[v]) continue; out[scc[u]]++; in[scc[v]]++; } } int ans1=0,ans2=0; for(int i=1; i<=sc; i++) { if(in[i]==0) ++ans1; if(out[i]==0) ++ans2; } if(sc==1) printf("1\n0\n"); else printf("%d\n%d\n",ans1,max(ans1,ans2)); return 0; }