最小斯坦纳树

解决一类图上存在 \(\mathcal O(\log)\) 量级关键点,使其连通的最小贡献子图的问题。


首先我们能轻易证明这个子图一定是树,因为断掉环不影响连通性。这棵树就是最小斯坦纳树。

我们不妨考虑状压 dp 求解。\(f_{i,S}\) 表示树根为 \(i\),我们已经在这棵树中连通了 \(S\) 这些关键点,产生的最小贡献。

考虑类似树形 dp 进行转移。我们首先可以合并子树:

\[f_{i,S}\gets \min\limits_{T\subset S} f_{i,T}+f_{i,S\backslash T} \]

其次我们可以转移树根(这里利用了不合法不优,我们不在意新树根是不是关键点):

\[f_{i,S}\gets f_{j,S}+w(j\to i) \]

然后考虑转移顺序。显然对于第一种转移,我们按照 \(S\) 从小到大转移就可以了。然后注意到对于每个 \(S\),第二种转移本质上是对 dp 数组搞了一遍最短路,于是我们拿 dij 把 dp 数组松弛一遍就好了。

这是带边权的写法。点权也是类似的,只有一点很小的细节上的变化。

[ABC395G] Minimum Steiner Tree 2

题意 给定一张完全图,有 $Q$ 个询问 $S,T$。查询包含 $\{i|1\le i\le K\}\cup \{S,T\}$ 的边权和最小斯坦纳树。

保证 \(K+1\le S,T\)

我们考虑状压 \(1\)\(K\) 这一部分的永久关键点,在状态最后带两个非关键点。换句话说,我们设 \(f_{S,i,j,k}\) 表示现在 \(1\)\(K\) 中的关键点连通了 \(S\),树根是 \(i\),存在两个非关键点 \(j,k\) 的最小边权和。

然后你发现我们状态数起飞了。考虑我们是否真的有必要存储根加上两个非关键点。显然,我们可以强行钦定树根是两个非关键点的其中一个,答案仍然会是合法的状态。

于是我们现在已经直接就能做有一个非关键点的问题了(其实和原来的 dp 没有一点区别)。

考虑还有一个关键点怎么办。比较搞笑的是,这里的解决方式是直接暴力:我们直接把所有非关键点一个一个塞进关键点集合,做 \(n\) 次一个非关键点的最小斯坦纳树(也就是本身)把答案预处理出来即可。简单预处理最短路,时间复杂度 \(\mathcal O(n^23^k+n^32^k+q)\),可以通过。

P3264 [JLOI2015] 管道连接

这里终于出现了一点不一样,我们可以不把所有关键点连在一起,只需要把同色的关键点连在一起就好了。

于是考虑简单地增加一条转移就行:当我们的状态里面关键点构成若干个颜色的全集的并,我们可以切换根而不做路径的贡献,因为新的根根本没有必要和目前的子图连通。

P4294 [WC2008] 游览计划

点权最小斯坦纳树,附加输出方案。

由于我的这个手太懒了,我不想写输出方案,所以不写了。

P7450 [THUSCH 2017] 巧克力

还有人类吗。

考虑先不管中位数的事,我们怎么解决第一问这个“至少包含 \(k\) 种颜色”的限制。

如果我们能给出这 \(k\) 种颜色,那么实际上就是最小斯坦纳树的模板改一改就好了。但是实际上我们不可能枚举所有可能的颜色集合,那怎么办呢?

介绍一个叫 color-coding 的 trick:实际上我们随机地把所有颜色映射到 \([0,k)\) 当中,然后按照只有这些颜色来做是正确的。考虑一个答案连通块何时可以被算到:当且仅当它身上有完整的 \([0,k)\) 所有颜色时。我们直接考虑这个连通块上原本的颜色有 \(k^k\) 种染法,答案的染法是 \(k!\) 种,一次独立随机映射颜色的正确率是 \(\frac{1}{26}\)

可以证明,如果答案连通块中包含超过 \(k\) 种颜色,概率不会变小。综上所述,这个随机化的正确率并不差,我们随机映射 \(150\)\(250\) 次就很难出错了。

现在我们解决了第一问,那么第二问也不难了。trickily,我们考虑二分中位数,把小于中位数的美味值设置为 \(-1\),大于中位数的美味值设成 \(1\),那么我们在 dp 中最小化连通块大小的同时最小化权值和就好了。网格图上用 SPFA 写最短路没有问题。

然后注意一下写法。二分套随机化的写法我觉得比较合理,因为随机化套二分正确性不是很严谨(考虑二分相当于同一种映射多测 \(\log\) 次),需要增加次数,然后疑似就会被卡常,需要在二分时剪枝。

当然不是说前面一个写法不被卡常的意思。注意到我们需要的比较和运算的性质恰好和整数的比较和运算是相同的,所以我们考虑 dp 时把两维压在一起变成一个大进制数来做,也就是把权值设置成 \(10^3+2\)\(10^3\),这样前面的部分就表示连通块中的点数,后面的部分就用来间接地表示权值和。

然后注意一下清空的复杂度,不要直接 memset dp 数组。因为实际上我们根本用不到 \(233\times 233\) 大小。

posted @ 2025-07-28 14:54  Shunpower  阅读(69)  评论(0)    收藏  举报