二分专题训练
二分问题用于求解直接求解答案很难,但验证一个答案是否符合条件很容易,且答案具有单调性的这类问题。注意二分到的答案一定要想办法用到判别函数中。
- 题目描述:给\(n\)个物品,每个物品有两个属性\(v_i\)和\(c_i\),选出其中\(m\)件,最大化\(\frac{\sum v_i}{\sum c_i}\)。
- 数据范围:\(1≤m≤n≤200\),\(1≤c_i,v_i≤1×10^4\)。
01分数规划的板子题,不过很久没写过了还是记录一下。
对于一个数值\(\lambda\),验证其是否符合条件是更为容易的。例如我们需要验证是否存在一种选法使得\(\frac{\sum v_i}{\sum c_i}\ge \lambda\),由于\(c_i\)非负,简单变换一下就是\(\sum\left(v_i-\lambda c_i\right)\ge 0\),那么将\(v_i-\lambda c_i\)排序后看最大的\(m\)个之和是否非负即可。
时间复杂度为\(O(n\log v)\),\(v\)为值域。
- 题目描述:给定\(n\)个数\(\{a_n\}\),现在要选出一些数,满足任意两个相邻的数中至少有一个数被选择。求出所有选择方案中,被选中数字平均值和中位数的最大值(偶数\(2k\)个数的中位数视作第\(k\)小的数) 。
- 数据范围:\(2\le n\le 10^5\),\(1\le a_i\le 10^9\)。
感觉这类求平均值的题目二分的可能性很大。
对平均值,需最大化\(\frac{\sum a_{i}}{k}\)(\(k\)是选数的数量),是一个01分数规划的模型,对于\(\frac{\sum a_{i}}{k}\ge\lambda\)可以转换为\(\sum(a_i-\lambda)\ge0\),记\(c_i=a_i-\lambda\),看是否存在一种选法使\(\sum c_i\ge 0\)即可。
令\(f_i\)为目前到\(i\)且必须选\(i\)能取得的最大\(\sum c_i\)值,那么转移为\(f_i=\max\{f_{i-1},f_{i-2}\}+c_i\),最后\(\max\{f_{n-1},f_{n}\}\)即是答案。
对中位数,验证某数\(\lambda\)时,将小于其的数置为\(-1\),大于等于其的数置为\(1\),还是按照上述思路进行dp,如果最后答案大于0(不能有等于,题目中明确第\(k\)小的才是中位数),说明存在一种选法选出的中位数\(\ge \lambda\),此时增加二分下限即可。
时间复杂度为\(O(n\log v)\)。
- 给一个\(n\)点\(m\)边的无向带权连通图,每条边是黑色或白色。求一棵最小权的恰好有\(need\)条白色边的生成树。
- 数据范围:\(n\le5\times 10^4\),\(m\le10^5\),边权\(\in[0,100]\)。
一道WQS二分的板子题(不过之前没接触过)。
令\(g_x\)为恰好选\(x\)条白边的最小生成树权值,将\((x,g_x)\)按折线图的方式画出来,是一个向下凸的凸包,即其斜率应是单调增加的(意思是能与\((x,g_x)\)相切的直线斜率随\(x\)的增大而增大)。斜率满足单调性,就是可以二分的。对于某次二分得到的斜率\(k\),求出此时相切的点并与题目中要求的点数作比较即可。
令\(f_x=g_x-kx\),即斜率为\(k\)的直线过\((x,g_x)\)时的截距,由于相切点对应的截距最小,也即找到\(x\)使其能最小化\(f_x=g_x-kx\)。按照\(g_x\)的定义,\(g_x=\sum_{i\in W}^xw_i+\sum_{i\in B}^{n-1-x}w_j\),那么\(f_x=\sum_{i\in W}^x(w_i-k)+\sum_{i\in B}^{n-1-x}w_j\),也就是说我们把所有的白边权值都减去\(k\),做一次无约束的Kruskal,统计此时有多少次白边,最终得到的白边数就是相切的那个点。
注意如果二分的判别条件是相切点大于等于\(need\),那么当白边和黑边边权相等时要优先考虑白边,反之优先考虑黑边,因为我们验证的是存在性,存在一组白边符合条件即可。
时间复杂度为\(O(m\log v\log mn)\)。
- 给定一个\(n\)点\(m\)边的单向带权图,点权为\(f_i\),边权为\(T_j\)。现从任意一点出发,取经过的所有点的点权和\(\sum f_i\)(重复的点只算一次),所有边的边权和\(\sum T_j\)(重复的边每次都需计算),最终需回到出发点,最大化\(\frac{\sum f_i}{\sum T_j}\)。
- 数据范围:\(2\le n \le 1000\),\(2\le m \le 5000\),\(1\le f_i,T_j\le 1000\)。
其实也是01分数规划的模型,不过我没有意识到经过的边数和点数实际上是一样的。
想要使用01分数规划模型,首先需要证明一个点被经过两次肯定是不优的,假设一个点\(x\)连接两个环,其总点权和总边权分别为\(f_1,T_1\)和\(f_2,T_2\),那么需要证明\(\frac{f_1}{T_1}>\frac{f_1+f_2-f_x}{T1+T2}\),证明过程参考这里。
有了这个东西后,我们知道最优解中一个点最多被经过一次,那么点数和边数相等,用01分数规划能转换成一个判断负环的题目。
最坏情况下,时间复杂度为\(O(n^2\log v)\)。
这个之前写过了,具体看这篇,不过再写还是觉得细节好麻烦。
- 题目描述:给定一颗\(n\)个点的树,初始时1号节点为黑色,其余是白色。A、B两人轮流操作,开始时B在1号节点。每一轮,A选择\(k\)个点染黑,然后B走到一个相邻节点,如果B当前处于白点则B胜,否则当A将所有点染为黑点时A胜。求能让A获胜的最小的\(k\)。
- 数据范围:\(n\le3\times 10^5\)。
答案具有单调性,因此可以用二分转化为判别性问题。考虑如何判断某答案\(k\)是否能使A获胜。
首先站在B的角度看,B是不可能走回头路的,因为这样和直接走另一条路的效果相同且还让A多了几次染色的机会。那么B会从1号节点一直顺着往叶节点走,A需要在B走之前将B所在节点的所有子节点都染黑。
对于子节点数量\(>k\)的点,在一次染色中无法把所有的子节点染成黑色,需要通过染祖先节点时剩余的染色次数来染。记\(f_i\)表示以\(i\)为根的子树缺少的染色次数(\(f_i\le0\)就表示不需要补充染色了),那么转移方程为\(f_i=|\mathrm{son}_i|-k+\sum_{j\in \mathrm{son}_i}\max\{0,f_j\}\),其中\(\mathrm{son}_i\)是\(i\)的儿子集合,最后\(f_1\le0\)则A能获胜。
时间复杂度\(O(n\log n)\)。