本博客以一种较为少见的方式来解释WQS二分。
题目
首先,WQS二分用于解决什么问题?
我们先看一个伞兵题目:
有一个 n n n 个数的数组 a a a。
求在 a a a 中恰好选择 m m m 个数的情况下,选择的数的和的最大值。
你现在看到了这个题目,你发现你不会排序,不会贪心。好难啊,你想。
你打算用另一种方式解决这题。
做法
我们定义 g ( x ) g(x) g(x) 为恰好选择 x x x 个数的情况下,选择的数的和的最大值。
经过观察,我们发现 g ( x ) g(x) g(x) 是一个凸函数。
这表明,
g
(
x
)
g(x)
g(x) 只有一个最大值(也就是把所有正数都取了的情况)。
但你取不到,因为他不一定是 m m m。
如果我们不考虑 m m m 的限制,肯定会取这个最大值。
那么我们考虑如何做出改变使得不用考虑 m m m 的限制。
考虑给选择数一个惩罚或奖励 k k k,我们每次选择一个数,答案就增加 k k k。(谁告诉你 k k k 不能是负数了)
于是,这个函数 g ( x ) g(x) g(x) 就转变成另一个函数 f ( x ) = g ( x ) + k x f(x) = g(x) + kx f(x)=g(x)+kx 了。
显然,当 k k k 取值增大, f ( x ) f(x) f(x) 的最大值取值位置就往右移动。当 k k k 取值减少, f ( x ) f(x) f(x) 最大值取值位置往左移动。
而我们能够轻而易举的对于每个 k k k 计算 f ( x ) f(x) f(x) 最大值取值位置和 f ( x ) f(x) f(x) 最大值( O ( n ) O(n) O(n))。
- 由于 f ( x ) f(x) f(x) 是选择 x x x 个时的最大值,那么 f ( x ) f(x) f(x) 的最大值就是选择任意个的最大值,相当于取消了最大值限制。
所以,我们可以二分 k k k,根据当前 k k k 下 f ( x ) f(x) f(x) 最大值取值位置与题目限制 m m m 的大小关系来更改 k k k。
最终可以获得最大值取值位置为 m m m 时的 k k k 和 f ( m ) f(m) f(m)。
然后通过 f ( x ) = g ( x ) + k x f(x) = g(x) + kx f(x)=g(x)+kx,可以反推出 g ( m ) = f ( m ) − k m g(m) = f(m) - km g(m)=f(m)−km。
做完了,
O
(
n
log
n
)
O(n\log n)
O(nlogn)。我们震惊的发现时间复杂度和原做法没区别。
好,想必到这里你大致看懂WQS二分的用法或者大致知道WQS二分时什么东西了。
为了方便理解,我们再来一道题(一道需要用到WQS二分的题目)。
题目
有一张 n n n 个点, m m m 条带权边的图 G G G,每条边权值为 w i w_i wi。
求图 G G G 的生成树的最大权值和,满足点 s s s 恰好选择 t t t 条边。
首先发现本题难以使用 d p dp dp 之类算法来做。
其次,本题取消限制后极其容易。
最后,容易发现 ,关于
t
t
t 的函数
g
(
t
)
g(t)
g(t) 代表选择
t
t
t 条时的答案,是一个凸函数。
我们就可以使用WPS二分以 O ( log n ) O(\log n) O(logn) 的代价换取取消限制。
做法
还是一样,我们取消限制,然后增加奖励或惩罚 k k k。
根据取最大值位置与 t t t 的关系,二分惩罚 k k k,直接使用最小生成树 O ( n ) O(n) O(n) 来计算惩罚为 k k k 时最大值。
最终可以得出在一定惩罚下,取 m m m 条边时的最大值。我们只需要减去惩罚即可。
也可以换一种方法理解。
我们将
g
(
x
)
g(x)
g(x) 的图像画出,是一个凸函数。
我们现在需要求
x
=
t
x=t
x=t 时的取值
g
(
t
)
g(t)
g(t)。
我们发现对于每个在 g ( x ) g(x) g(x) 上的点 ( x , g ( x ) ) (x, g(x)) (x,g(x)),都存在一个斜率 k k k,使得经过这个点的斜率为 k k k 的直线 l l l 能与 g ( x ) g(x) g(x) 相切。
因为 g ( x ) g(x) g(x) 是一个凸函数,所以随着 x x x 的增大, ( x , g ( x ) ) (x, g(x)) (x,g(x)) 对应斜率 k k k 会随之增大。
所以我们可以通过二分斜率 k k k,来求出 x = t x=t x=t 时的取值 g ( t ) g(t) g(t)。
对于每次二分的斜率 k k k,我们要求 g ( x ) g(x) g(x) 中切线斜率为 k k k 的切点。我们设直线 l l l 的 y y y 轴截距为 f ( x ) f(x) f(x),代表斜率为 k k k 的直线 l l l 经过点 ( x , g ( x ) ) (x, g(x)) (x,g(x)) 时的 y y y 轴截距。
则 g ( x ) = k x + f ( x ) g(x) = kx + f(x) g(x)=kx+f(x)。
容易发现,当 f ( x ) f(x) f(x) 取到最大值时, y = k x + f ( x ) y=kx+f(x) y=kx+f(x) 就会与 g ( x ) g(x) g(x) 函数相切,切点则是此时的 ( x , g ( x ) ) (x, g(x)) (x,g(x))
所以我们尝试求出 f ( x ) f(x) f(x) 最大值,从 g ( x ) = k x + f ( x ) g(x) = kx + f(x) g(x)=kx+f(x) 可得 f ( x ) = g ( x ) − k x f(x) = g(x) - kx f(x)=g(x)−kx。
这个式子可以感性理解为: f ( x ) f(x) f(x) 为将与 s s s 相连的边中所选的 x x x 条边的权值都减少 k k k 时,选择 x x x 条边时的生成树的最大权值和。
所以 f ( x ) f(x) f(x) 中最大值则为:将与 s s s 相连的边中所选的边的权值都减少 k k k 时,最大生成树的权值和。
所以我们可以非常容易的求出 f ( x ) f(x) f(x) 最大值,和取得最大值时选择的边数 x x x。
我们就可以开始二分了。
对于最终求出的 f ( t ) f(t) f(t),我们可以根据 g ( x ) = k x + f ( x ) g(x) = kx + f(x) g(x)=kx+f(x),求出 g ( t ) g(t) g(t)。
总结
经过上面几题可以发现,WQS二分适用于符合一下几点的问题:
- 有一个恰好选择的个数限制 m m m
- 代表恰选 x x x 个时答案的函数 g ( x ) g(x) g(x) 需要为凸函数。
- 取消限制 k k k 后问题可以快速计算。
解决时,一般选取一个附值 k k k,使得每多选一次,最终答案就增加 k k k。然后计算当附值为 k k k 时的最大值取值位置 x 0 x_0 x0。将其与限制 m m m 像比较。由于函数是凸函数,附值为 k k k 时的取最大值位置 h ( k ) = x 0 h(k) =x_0 h(k)=x0 有单调性。所以可以二分 k k k 让 x 0 x_0 x0 靠近 m m m,最终计算得当附值为 k 0 k_0 k0 是取最大值位置 h ( k 0 ) = m h(k_0)=m h(k0)=m 时。再将附值为 k 0 k_0 k0 时的最大值减去 k × m k\times m k×m 即可。