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

\(Link\)

Description

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

Solution

先跑一遍\(Tarjan\)缩点。第一问的答案就是缩点后入度为零的点的数量,而第二问则是入度为零和出度为零的点的数量的较大值。

第一问很好理解,而第二问是为什么呢?

我的理解是,第二问就是要让我们把缩点后的若干个强连通分量用一些边连起来,使它们形成一个强连通分量。那么显然每个点的入度和出度都不能为零。我们肯定是把出度为零的点连到入度为零的点上去。然后还剩下一些入度为零或出度为零的就单独搞。所以就是取较大值了。

Code

#include <bits/stdc++.h>

using namespace std;

int n, tot, ind, top, cnt, tot1, tot2, res1, res2, rd[10005], cd[10005], col[10005], low[10005], dfn[10005], que[10005], vis[10005], hd[10005], to[5000005], nxt[5000005];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * fl;
}

void add(int x, int y)
{
	tot ++ ;
	to[tot] = y;
	nxt[tot] = hd[x];
	hd[x] = tot;
	return;
}

void Tarjan(int x)
{
	low[x] = dfn[x] = ++ ind;
	que[ ++ top] = x;
	vis[x] = 1;
	for (int i = hd[x]; i; i = nxt[i])
	{
		int y = to[i];
		if (!dfn[y])
		{
			Tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if (vis[y]) low[x] = min(low[x], dfn[y]);
	}
	if (low[x] == dfn[x])
	{
		cnt ++ ;
		int now = -1;
		do
		{
			now = que[top -- ];
			col[now] = cnt;
			vis[now] = 0;
		} while (now != x);
	}
	return;
}

int main()
{
	n = read();
	for (int i = 1; i <= n; i ++ )
	{
		while (1)
		{
			int x = read();
			if (!x) break;
			add(i, x);
		}
	}
	for (int i = 1; i <= n; i ++ )
		if (!dfn[i])
			Tarjan(i);
	for (int x = 1; x <= n; x ++ )
	{
		for (int i = hd[x]; i; i = nxt[i])
		{
			int y = to[i];
			if (col[x] != col[y])
			{
				add(col[x], col[y]);
				rd[col[y]] ++ ;
				cd[col[x]] ++ ;
			}
		}
	}
	for (int i = 1; i <= cnt; i ++ )
	{
		tot1 += (!rd[i]);
		tot2 += (!cd[i]);
	}
	res1 = tot1; res2 = max(tot1, tot2);
	printf("%d\n%d\n", res1, res2);
	return 0;
}
posted @ 2020-11-03 08:50  andysj  阅读(55)  评论(0编辑  收藏  举报