【题解】Network

「CEOI2012」Network


Solution to Question

首先缩点(当然也可以不缩?),然后跑一遍 DFS 即可。

//w为联通分量里的节点个数
inline void dfs (const int &u) {
	ans1[u] = w[u];
	for (int v : G_scc[u])
		dfs(v), ans1[u] += ans1[v];
}

Solution to Question

观察缩完点后的图一定是保证无环的,又由于题目有个非常特殊的条件:图中有一个中心点 \(r\),对于其它所有点,有且仅有一条路径可达。于是,缩完点后的图一定是一棵以 \(r\) 为根节点的

对于节点 \(u\),它的子树中的节点想要到达其它子树,就必须往上爬,于是有了一个贪心的思路:「每一次连边都需要尽量往上连」。


注意这里的到达是指有且仅有一条路径,考虑下图情况:如果我们想从 \(C\)\(A\) 甚至更上面连边,此时由于 \(A,B\) 在同一个联通分量中,\(B\) 原来是可以达到 \(A\) 的,再连上这条边之后,\(B\) 就有 \(2\) 条路径可以到达 \(A\)\(A,B,C\) 又形成了一个新的环),是不符合题意的。

情况一

但是对于如下图「特例」,向上连的边,仅是“擦边”经过联通分量。此时这条边是可连的。

情况二


对于节点 \(u\),它的子节点有如下两种情况:(下图均为缩完点后的图)

  • 所有节点都连接到 \(u\),再由 \(u\) 连一条边向上。

  • 有且 仅有一条 边直接跳过 \(u\),直接向上连边,且满足「特例」(见上)。

    「仅有一条」:因为如果有第二条边向上连,则会有两个环经过同一条路径(\(u\) 往上)。


实现

由于缩完点是一棵树,考虑用 \(edge[]\) 记录下每个节点往上的那条边起始点在原图中的编号。

建图时,加上:

edge[scc[v]] = make_pair(u, v), 

这样我们可以在 \(O(1)\) 的时间复杂度,判断相邻的三个连通分量 \(A,B,C\),是否只经过位于中间的连通分量 \(B\) 中的一个点。

edge[B].second == edge[C].first

Summary

  1. 这道题需要考虑很多情况;
  2. 实现起来也还是有点难度。
posted @ 2023-07-11 20:57  CloudWings  阅读(25)  评论(0编辑  收藏  举报