题解 [USACO5.3] Network of Schools

考虑缩点。

对缩点后的图:
会变成外向树/内向树森林,或者DAG。
对于入度为0的点需要放一个文件。
将入度为0和出度为0的点连边为最优的构建强连通图的策略。则第二个子问题的答案为入度为0与出度为0的点数目的最大值。
注意特判缩点后成为一个点的情况。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 123, M = N*N;
int head[N], ver[M], nex[M], my[M], tot;
inline void addedge(int u, int v) {
	ver[tot] = v; nex[tot] = head[u]; my[tot] = u; head[u] = tot++;
}

int dfn[N], low[N], cl[N], sta[N], num[N], ind[N], outd[N], col = 1, dfs_clock = 1, top = 0;
void dfs(int cur) {
	dfn[cur] = low[cur] = dfs_clock++;
	sta[top++] = cur;
	for(int i = head[cur]; ~i; i = nex[i]) {
		if(!dfn[ver[i]]) {
			dfs(ver[i]);
			low[cur] = min(low[cur], low[ver[i]]); 
		} else if(!cl[ver[i]]) {
			low[cur] = min(low[cur], dfn[ver[i]]);
		}
	}
	if(dfn[cur] == low[cur]) {
		--top;
		while(sta[top] != cur) {
			++num[col];
			cl[sta[top]] = col;
			--top;
		}
		cl[cur] = col;
		++num[col];
		++col;
	}
}

int main() {
	memset(head, -1, sizeof(head));
	int n, tmp;
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) {
		scanf("%d", &tmp);
		while(tmp) {
			addedge(i, tmp);
			scanf("%d", &tmp);
		}
	}
	for(int i = 1; i <= n; ++i) if(!dfn[i]) dfs(i);
	for(int i = 0; i < tot; ++i) {
		if(cl[my[i]] != cl[ver[i]]) {
			++ind[cl[ver[i]]]; ++outd[cl[my[i]]];
		}
	}
	if(col == 2) {
		printf("1\n0\n");
		return 0;
	}
	int ans1 = 0, ans2 = 0;
	for(int i = 1; i < col; ++i) {
		if(!ind[i]) ++ans1;
		if(!outd[i]) ++ans2;
	}
	ans2 = max(ans1, ans2);  
	printf("%d\n%d\n", ans1, ans2);
	return 0;
}