AcWing 367. 学校网络(有向图的强连通分量)

题目

传说中的暗之连锁被人们称为 Dark。

Dark 是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。

经过研究,你发现 Dark 呈现无向图的结构,图中有 N 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。

Dark 有 N – 1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。

另外,Dark 还有 M 条附加边。

你的任务是把 Dark 斩为不连通的两部分。

一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。

一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。

但是你的能力只能再切断 Dark 的一条附加边。

现在你想要知道,一共有多少种方案可以击败 Dark。

注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。

输入格式

一些学校连接在一个计算机网络上,学校之间存在软件支援协议,每个学校都有它应支援的学校名单(学校A支援学校B,并不表示学校B一定要支援学校A)。

当某校获得一个新软件时,无论是直接获得还是通过网络获得,该校都应立即将这个软件通过网络传送给它应支援的学校。

因此,一个新软件若想让所有学校都能使用,只需将其提供给一些学校即可。

现在请问最少需要将一个新软件直接提供给多少个学校,才能使软件能够通过网络被传送到所有学校?

最少需要添加几条新的支援关系,使得将一个新软件提供给任何一个学校,其他所有学校就都可以通过网络获得该软件?

输出格式

输出两个问题的结果,每个结果占一行。

数据范围

2≤N≤100

思路

问题1肯定是输出缩点后,入度为0的点。
问题2:
看能分成几个连通分量,如果分成一个,直接输出0就可。
否则输出入度和出度的点的最大值。
因为只要将出度为0的点和入度为0的点一一相连就能变成强连通图。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;

const int N=110,M=10010;

int n;
int h[N],e[M],ne[M],idx;
int dfn[N],low[N],timestamp; 
int stk[N],top;
bool in_stk[N];
int id[N],scc_cnt,Size[N];
int din[N],dout[N];

void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void tarjan(int u)
{
	dfn[u]=low[u]=++timestamp;
	stk[++top]=u,in_stk[u]=true;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(!dfn[j])
		{
			tarjan(j);
			low[u]=min(low[u],low[j]);
		}
		else if(in_stk[j]) low[u]=min(low[u],dfn[j]);
	}
	
	if(dfn[u]==low[u])
	{
		++scc_cnt;
		int y;
		do{
			y=stk[top--];
			in_stk[y]=false;
			id[y]=scc_cnt;
			Size[scc_cnt]++;
		}while(y!=u);
	}
	
}

int main()
{
	//freopen("test.in","r",stdin);//设置 cin scanf 这些输入流都从 test.in中读取
    //freopen("test.out","w",stdout);//设置 cout printf 这些输出流都输出到 test.out里面去
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	memset(h,-1,sizeof h);
	for(int i=1;i<=n;i++)
	{
		int x;
		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=h[i];~j;j=ne[j])
		{
			int k=e[j];
			int a=id[i],b=id[k];
			if(a!=b)
			{
				dout[a]++;
				din[b]++;
			}
		}
	}
	int ans1=0,ans2=0;
	for(int i=1;i<=scc_cnt;i++)
	{
		if(!din[i]) ans1++;
		if(!dout[i]) ans2++;
	}
	cout<<ans1<<endl;
	if(scc_cnt==1) cout<<0<<endl;
	else cout<<max(ans1,ans2)<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44828887/article/details/107348686