洛谷P2746 校園網Network of Schools
題目描述
一些學校連入一個電腦網絡。那些學校已訂立了協議:每個學校都會給其它的一些學校分發軟件(稱作“接受學校”)。註意即使 \(B\) 在 \(A\) 學校的分發列表中, \(A\) 也不一定在 \(B\) 學校的列表中。
你要寫一個程序計算,根據協議,為了讓網絡中所有的學校都用上新軟件,必須接受新軟件副本的最少學校數目(子任務 \(A\))。更進一步,我們想要確定通過給任意一個學校發送新軟件,這個軟件就會分發到網絡中的所有學校。為了完成這個任務,我們可能必須擴展接收學校列表,使其加入新成員。計算最少需要增加幾個擴展,使得不論我們給哪個學校發送新軟件,它都會到達其余所有的學校(子任務 \(B\)
輸入輸出格式
輸入格式:
輸入文件的第一行包括一個整數 \(N\):網絡中的學校數目(\(2 <= N <= 100\))。學校用前 \(N\) 個正整數標識。
接下來 \(N\) 行中每行都表示一個接收學校列表(分發列表)。第 \(i+1\) 行包括學校 \(i\) 的接收學校的標識符。每個列表用 \(0\) 結束。空列表只用一個 \(0\) 表示。
輸出格式:
你的程序應該在輸出文件中輸出兩行。
第一行應該包括一個正整數:子任務 \(A\) 的解。
第二行應該包括子任務 \(B\) 的解。
輸入輸出樣例
輸入樣例#1:
5 2 4 3 0 4 5 0 0 0 1 0
輸出樣例#1:
1
2
說明
題目翻譯來自NOCOW。
USACO Training Section 5.3
思路:對於子任務A,明顯,如果沒有連向某個點的邊,肯定就要讓這個點加入計劃,否則無法使整張圖組成一個強連通分量,因此,子任務A的答案即為入度為0的點的個數,對於子任務B,考慮我們用出度為0的點向入度為0的點連邊,使整張圖強連通,一個漏下也不滿足條件,所以子任務B的答案為出度為0的點的個數和入度為0的點的個數的最大值。然後如果幾個點已經構成了一個強連通分量,那麽它們在整張圖中只發揮一個點的作用,所以可以考慮縮點,然後記錄縮點之後的入度和出度,輸出兩個答案即可。
代碼:
#include<cstdio> #include<iostream> #include<stack> #define maxn 101 using namespace std; int n,num,js,cnt,rd[maxn],cd[maxn],head[maxn],dfn[maxn],low[maxn],bel[maxn],size[maxn]; int x1,x2; bool vis[maxn]; struct node { int v,nxt; }e[10001]; inline void ct(int u, int v) { e[++num].v=v; e[num].nxt=head[u]; head[u]=num; } inline int maxx(int a, int b) {return a>=b?a:b;} stack<int>q; void tarjan(int u) { dfn[u]=low[u]=++cnt; q.push(u),vis[u]=1; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].v; if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]); else if(vis[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { int x=-1;js++; while(x!=u) { x=q.top(),q.pop(); bel[x]=js,size[js]++; vis[x]=0; } } } int main() { scanf("%d",&n); for(int i=1,x;i<=n;++i) { while(scanf("%d",&x)==1) { if(!x) break; ct(i,x); } } for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int k=1;k<=n;++k) { for(int i=head[k];i;i=e[i].nxt) { int v=e[i].v; if(bel[k]!=bel[v]) ++cd[bel[k]],++rd[bel[v]]; } } for(int i=1;i<=js;++i) { if(!cd[i]) ++x1; if(!rd[i]) ++x2; } printf("%d\n%d\n",x2,max(x1,x2)); return 0; }
洛谷P2746 校園網Network of Schools