洛谷 P2746 [USACO5.3]校园网Network of Schools (缩点)

题目链接

本题的第一问的确很模板,但第二问却没有那么简单。看很多题解都直接贴结论,就算有讲解的也极其简略,这里写一篇较详细的

首先,将原图进行缩点,形成一个由若干个连通块组成的DAG。

我们先考虑单个连通块的情况:
很显然是 出度为零的点数与入度为零的点数的最大值,因为一条边可以给两个(一个入度为零一个出度为零)点做出贡献。
这样,不管你从哪个点开始,你都可以走到一个末端点 x x x(出度为零),从而走到一个与 x x x 连通的一个初始点(入度为零)。接下来,因为你在一个初始点,所以你可以走到该连通块内所有的末端点。又因为这所有的末端点都跟初始点间有连边,所以你就可以到达连通块中的任意一个点,也就形成了强连通分量。

那么,多连通块的时候也是同理。我们设有 k k k 个连通块,第 i i i 个连通块为 a i a_i ai。那么,对于每个 i i i 我们将 a i a_i ai 的末端点按照上面的方法连向 a i + 1 a_{i+1} ai+1 的初始点。特别的,将 a k a_k ak 的末端点连向 a 1 a_1 a1 的出端点。
这样,每个 a i a_i ai 末端点都可以到达 a i + 1 a_{i+1} ai+1 的初始点,从而到达 a i + 1 a_{i+1} ai+1 的端点,也就形成了强连通分量。

最后,第二问的答案就是全图中(缩点后)末端点数与初始点数的最大值

#include 
#include
#include
const int Maxn=100+10,inf=0x3f3f3f3f;
int n,timecnt,cnt; // 一年前写的代码,很丑,仅供参考
int dis[Maxn],ind[Maxn];
int dfn[Maxn],low[Maxn],f[Maxn];
bool vis[Maxn];
std::vector <int> e[Maxn],g[Maxn];
struct _stack{
     
	int s[Maxn],tot=0;
	inline int size(){
     return tot;}
	inline int top(){
     return s[tot];}
	inline void push(int x){
     s[++tot]=x;}
	inline void pop(){
     s[tot--]=0;}
};
_stack stack;
int read()
{
     
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
     if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
void tarjan(int x)
{
     
	dfn[x]=low[x]=++timecnt;
	stack.push(x),vis[x]=1;
	for(int i=0;i<e[x].size();++i)
	{
     
		int y=e[x][i];
		if(!dfn[y])
		{
     
			tarjan(y);
			low[x]=std::min(low[x],low[y]);
		}
		else if(vis[y])low[x]=std::min(low[x],low[y]);
	}
	if(dfn[x]==low[x])
	{
     
		while(stack.size())
		{
     
			int y=stack.top();
			stack.pop();
			f[y]=x,vis[y]=0;
			if(y==x)break;
		}
	}
}
int main()
{
     
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	n=read();
	for(int x=1;x<=n;++x)
	{
     
		int y=read();
		while(y)
		{
     
			e[x].push_back(y);
			y=read();
		}
	}
	for(int i=1;i<=n;++i)
	if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;++i)
	{
     
		for(int j=0;j<e[i].size();++j)
		{
     
			int x=i,y=e[i][j];
			if(f[x]==f[y])continue;
			g[f[x]].push_back(f[y]);
			ind[f[y]]++;
		}
	}
	int cnt1=0,cnt2=0;
	for(int i=1;i<=n;++i)
	{
     
		if(f[i]!=i)continue;
		cnt++;
		if(!g[i].size())cnt1++;
		if(!ind[i])cnt2++;
	}
	if(cnt==1)
	{
     
		printf("1\n0\n");
		return 0;
	}
	printf("%d\n%d\n",cnt2,std::max(cnt1,cnt2));
	return 0;
}

你可能感兴趣的:(题解,#洛谷,缩点,二分图)