题解 P2812 【校园网络【[USACO]Network of Schools加强版】】

浙江省的几所OI强校的神犇发明了一种人工智能,可以AC任何题目,所以他们决定建立一个网络来共享这个软件。但是由于他们脑力劳动过多导致全身无力身体被♂掏♂空,他们来找你帮助他们。

共有n所学校(n<=10000)已知他们实现设计好的网络共m条线路,为了保证高速,网络是单向的。现在请你告诉他们至少选几所学校作为共享软件的母机~~母鸡,能使每所学校都可以用上。再告诉他们至少要添加几条线路能使任意一所学校作为母机母鸡~~都可以使别的学校使用上软件。

前置知识:

分析

这道题的话,我们先考虑缩短。

不会缩点的可以看一下我的文章

既然我们缩好点了,那么整张图变成了一个 D A G DAG DAG(有向无环图)

这样就好处理了。

  • 对于问题 A

    我们发现既然这整张图是 D A G DAG DAG,那么答案显然为入度为 0 0 0 的点的个数

  • 对于问题 B
    我们发现这整张图是 D A G DAG DAG。我们要把它变成连通图。

    连通图需要满足:

    • 没有入度为 0 0 0 的点
    • 没有出度为 0 0 0 的点

    考虑入度为 0 0 0 和 出度为 0 0 0 的点两两匹配,则需要匹配 max ⁡ { 入度为   0   的点 , 入度为   1   的点 \max\{ \texttt{入度为}\ 0\ \texttt{的点},\texttt{入度为}\ 1\ \texttt{的点} max{入度为 0 的点,入度为 1 的点 次。

一些细节

注意缩点后只有一个点的情况,本身就是连通的,所以 问题 B 的答案为 0 0 0

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值