原题链接:https://codeforc.es/contest/1707/problem/C[https://codeforc.es/contest/1707/problem/C]
(家里网络不是很好,最近主站经常上不去,只好上镜像了)
题目大意
给你一个错误的求最小生成树的算法:
1 | vis := an array of length n |
输入一个无重边无自环的联通无向图,且第 \(i\) 条边的边权为 \(i\)。问你调用 findMST(1), findMST(2), ..., findMST(n) 之后,哪些真的找到了 MST。
思路分析
比赛时毫无思路。第二次打 div1,这场的 A 太吓人了,想了老半天,中途一度想润,看到约好一起打的同学已经罚了一发了,只好硬着头皮打下去。结果最后还是掉分了呜呜呜,刚上的橙24小时不到又回去了呜呜呜。
扯歪了,回到这道题上来。
首先,这道题的边权设定,意味着边权两两不同,意味着 MST 有且只有一个。我们可以直接确定出这棵 MST 和不在 MST 里的是哪些边。
这个错误代码的特点是,它用了 DFS。DFS 意味着:访问到一个点后,回溯前,这个点所有的边都会被扫一遍。
因此,我们可以看出第一个问题:
如果边 \((u, v)\) 不在 MST 内,我们假设在 MST 中的边都能正常经过(或者说我们假设不合法边只有这一条)。
那在 DFS 经过 u 之后,必须在回到 u 之前先到达 v。换言之,MST 上必须存在一条不需要经过之前已经经过的点的路径,来从 u 到达 v。
也就是,从满足这个条件:把 v(或 u) 当作 MST 的根时,在 u(或 v) 子树内的点,的点开始 DFS,才可能满足这个要求。其它点必然在回溯到 u 或 v 时走到 (u, v) 这条边。
我们再观察一下 DFS 进行的方式:是从小到大遍历当前点的边。
那有没有可能说,尽管满足了上面的条件,也就是虽然存在一条 MST 上的路径能走到 v,但却选择了 (u, v) 的情形呢?
答案是不可能的,如果先选了 (u, v),那意味着 MST 内的那条边是更大的。那我们把 MST 内的那条边换成 (u, v),因为已经知道 u 到 v 有路径连着了,换成 (u, v) 后它依然是个生成树,而且边权和就更小了,这与我们已经确定的 MST 是矛盾的。
综上,我们可以说上面这个条件是一个充分且必要的条件。
换言之,你可以对于每条边求出能不经过这条边的 DFS 起点。最后扫一遍看看哪个点可以不经过任何不合法边就行了。
处理时,我们可以假定一个根,然后我们会看到两种情形:
前者,子树互相包含的情况。下面那个点直接取子树,上面的点就是除了子树内包含下面那个点的儿子的那棵子树外,其它的点都可以取。
后者,就是直接取二者的子树了。
用 dfs 序啊树状数组之类的东西搞一下就行了。我用的是链剖。
代码
1 |
|