題解 P2746 【[USACO5.3]校園網Network of Schools】

一些學校連入一個電腦網絡。那些學校已訂立了協議:每個學校都會給其它的一些學校分發軟件(稱作“接受學校”)。注意即使 BBAA 學校的分發列表中,AA 也不一定在 BB 學校的列表中。

你要寫一個程序計算,根據協議,爲了讓網絡中所有的學校都用上新軟件,必須接受新軟件副本的最少學校數目(子任務 A)。更進一步,我們想要確定通過給任意一個學校發送新軟件,這個軟件就會分發到網絡中的所有學校。爲了完成這個任務,我們可能必須擴展接收學校列表,使其加入新成員。計算最少需要增加幾個擴展,使得不論我們給哪個學校發送新軟件,它都會到達其餘所有的學校(子任務 B)。一個擴展就是在一個學校的接收學校列表中引入一個新成員。

前置知識:

分析

這道題的話,我們先考慮縮短。

不會縮點的可以看一下我的文章

既然我們縮好點了,那麼整張圖變成了一個 DAGDAG(有向無環圖)

這樣就好處理了。

  • 對於問題 A

    我們發現既然這整張圖是 DAGDAG,那麼答案顯然爲入度爲 00 的點的個數

  • 對於問題 B
    我們發現這整張圖是 DAGDAG。我們要把它變成連通圖。

    連通圖需要滿足:

    • 沒有入度爲 00 的點
    • 沒有出度爲 00 的點

    考慮入度爲 00 和 出度爲 00 的點兩兩匹配,則需要匹配 max{入度爲 0 的點,入度爲 1 的點\max\{ \texttt{入度爲}\ 0\ \texttt{的點},\texttt{入度爲}\ 1\ \texttt{的點} 次。

一些細節

注意縮點後只有一個點的情況,本身就是連通的,所以 問題 B 的答案爲 00

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>inline void write(T x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar('0'+x%10);
}
const int MAXN=1e6+10,MAXM=1e6+10;
int s[MAXN],stop,dfn[MAXN],low[MAXN],scccnt,sccnum[MAXN],dfscnt,tot,he[MAXN],ne[MAXM<<1],ed[MAXM<<1],n,x,se,es,du[MAXN],ud[MAXN];
void add(int x,int y){
	ed[++tot]=y;
	ne[tot]=he[x];
	he[x]=tot;
}
inline void tarjan(int now){
	dfn[now]=low[now]=++dfscnt;
	s[stop++]=now;
	for (int i=he[now];i;i=ne[i]){
		if(!dfn[ed[i]]){
			tarjan(ed[i]);
			low[now]=min(low[now],low[ed[i]]);
		}else if(!sccnum[ed[i]]){
			low[now]=min(low[now],dfn[ed[i]]);
		}
	}
	if(dfn[now]==low[now]){
		scccnt++;
		do{
			sccnum[s[--stop]]=scccnt;
		}while(s[stop]!=now);
	}
}//tarjin的板子
int main(){
	read(n);
	for(int i=1;i<=n;i++)
		while(cin>>x&&x)add(i,x);
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;i++)
		for(int j=he[i];j;j=ne[j])
			if(sccnum[i]!=sccnum[ed[j]]){
				du[sccnum[ed[j]]]++;//統計
				ud[sccnum[i]]++;//統計
			}
	for(int i=1;i<=scccnt;i++){
		if(!du[i])se++;//入度爲0的點
		if(!ud[i])es++;//出度爲0的點
	}
	cout<<se<<endl<<(scccnt==1?0:max(se,es));//小細節
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章