wqs 二分
介绍
wqs 二分是用来解决恰好(或者至少至多)选择 \(k\) 个物品的问题的一种算法(大部分题目是最优化问题)。
设 \(y_i\) 表示恰好选择 \(i\) 个物品的问题的最优解,如果将所有点 \((i,y_i)\) 画在平面直角坐标系上,这若干个点构成一个凸包,那么就可以运用 wqs 二分算法。
我们以上凸壳为例(即斜率逐渐减小),讲讲 wqs 二分的过程。
过程
我们用直线去切凸包,会得到一个切点,显然切点横坐标关于切线斜率的函数是单调的,所以我们可以二分斜率(大部分题目点的坐标是格点,所以只需要整数二分就行),得到切点,如果切点横坐标恰好为 \(k\) 就是答案。
现在问题难在给定斜率,求出切点横坐标。
现在已知斜率 \(k\),我们对于每一个点 \((i,y_i)\),利用点斜式写出经过 \((i,y_i)\) 且斜率为 \(k\) 的直线方程 \(y_i-b=ki\),可以得到截距 \(b=y_i-ki\)。显然能让 \(b\) 最大的 \(i\) 就是切点横坐标,因为 \(y_i\) 表示恰好选择 \(i\) 个物品的问题的最优解,所以 \(y_i-ki\) 表示将每个物品的代价 \(-k\),恰好选择 \(i\) 个物品的最优解。那么 \(b\) 就表示将每个物品的代价 \(-k\) 的最优解。
我们发现这样就把恰好去掉了,只需要在求最优解的同时把选择物品个数求出来就可以得到切点横坐标。
最后再通过二分得到的切线斜率求出答案。
这样做会遇到一些细节,例如一条线可能会切到多个点,我们只需要选择这多个点中横坐标最小 / 最大的一个,不然最后求答案可能会出问题。
这个细节情况也导致你无法求出方案,因为 \(k\) 可能是切到的若干个点的中间一个。但是大部分 wqs 二分的题目都不需要求方案,所以不用太在意。
P2619 [国家集训队] Tree I
打表注意到答案是下凸函数,所以考虑 wqs 二分。
二分斜率 \(k\),求切点时将每条白边权值 \(-k\),求最小生成树,求出最小生成树中的白边条数就是切点横坐标。
因为一条线可能会切到多个点,选择横坐标最小的点,所以排序的时候相同权值要让白边排在黑边后面。
P1484 种树
注意到答案是上凸函数,所以考虑 wqs 二分。
二分斜率 \(k\),求切点时将每颗树的权值 \(-k\),然后直接 dp,再记录一下选了多少颗树即可。
这样就能求出恰好种 \(m\) 颗树的答案。
但是题目中要求出最多种 \(m\) 颗树的答案,怎么做呢?先用水平的线切凸包求出最高点横坐标 \(p\),如果 \(m\ge p\),答案就是最高点纵坐标。否则答案就是恰好种 \(m\) 颗树的答案。
内部 dp 的写法一般用结构体封装,重载 < 和 +,这样写代码比较方便。
P4383 [八省联考 2018] 林克卡特树
去掉 \(k\) 条边,树就会变成 \(k+1\) 个连通块,最优方案肯定是把这 \(k+1\) 个连通块的直径拿出来拼在一起。所以题目就变成了选择恰好 \(k+1\) 条不相交的链,使得链长度和最大。
注意到答案是上凸函数,所以考虑 wqs 二分。
二分斜率 \(k\),求切点时考虑树形 dp,设 \(f_{u,i}\),分别表示以 \(u\) 为根的子树,以 \(u\) 为端点且被选择了的边的条数为 \(i\) 的最优解。
转移是简单的。
P4983 忘情
先简化式子,设 \(x\) 的前缀和数组是 \(s\),那么 \([i,j]\) 这一段的权值就是 \((s_j-s_{i-1}+1)^2\)。
题目转化为将长度为 \(n\) 的序列恰好分成非空的 \(m\) 段,使得每一段的权值和最小。
注意到答案是下凸函数,考虑 wqs 二分。
二分斜率 \(k\),求切点时考虑 dp,设 \(f_i\) 表示最后一段在 \(i\) 处结尾的最小答案,那么转移就是 \(f_i=\min_{j<i}\{f_j+(s_i-s_j+1)^2\}\),用斜率优化即可。

浙公网安备 33010602011771号