其实写这个主要是想解释一下它的原理,教程、习题什么的网上都有,比如这个。
就拿这题来讲吧。
首先我们画出一个函数 \(f(x)\) 表示 \(s\) 的度恰好为 \(x\) 时,最小生成树的权值和。
当然,这个函数只会取在某一些整点上,我们把它连起来就行了。
然后你会发现它是下凸的(凹的)。(证明不太会,在这里致歉,但我觉得这个记住就好了。另外我觉得上面博客链接的证明也是有点问题的。)
一个例子:
因为它是下凸的,所以两点之间的斜率是从左往右递增的。wqs二分的思想就是二分这个斜率,然后就能找到需要的 \(x\),比如这道题就要求度恰好为多少。
那这个题要怎么实现呢?我们先随便找一个斜率 \(k\) 试试吧:
然后我们发现那个与 \(y\) 轴交点最低的直线所属的点就是这个斜率属于的 \(x\)。
设过 \(x\) 位置的点的直线交 \(y\) 轴与 \((0,b_x)\),则有 \(f(x)=kx+b_x\),即 \(b_x=f(x)-kx\)。
因为要最小化 \(b_x\),所以要求 \(min\{f(x)-kx\}\) 的 \(x\)。
然后发现这个东西是可以构造的!考虑一个 \(s\) 的度为 \(x\) 的方案,那么我们只要让与 \(s\) 相连的边都加上 \(-k\) 就是 \(b_x\)!
因此,想要找到最小的 \(f(x)-kx\),只需给所有与 \(s\) 相连的边加上 \(-k\),跑一遍 mst,最后看有几个与 \(s\) 相连的边就好了。
二分斜率是 \(\mathcal{O}(\log w)\) 的,\(w\) 是边权范围。用 kruskal 求 mst 是 \(\mathcal{O}(m\log m + m\alpha(m))\) 的。但是实际上不用重新排序,可以用双指针把 \(\log\) 去掉。
所以总的是 \(\mathcal{O}(m\alpha(m)\log w)\)。