学习笔记/DP:wqs 二分概述
1. 概述
1.0. 概述
wqs 二分,即王钦石二分,是一种通过降维来优化 dp 的处理手段。在 OI 中,wqs 二分最常用于处理一类 2D/1D dp,常搭配斜率优化、决策单调性等其他 dp 优化方式使用,较为套路。
1.1. 适用题型
wqs 二分处理的题型: 选取若干个(组)物品,数量有限制,选取有代价,询问代价极值。
\(\text{eg.}\) 给定一数列 \(a_n\),要求将其按顺序分割为 \(c\) 组,使得每组的代价和最小,其中每组的代价定义为该组内所有数的和的平方。\(n,c\le10^5,a_i\ge1\)。
显然有 2D/1D 的 dp:设 \(f_{i,j}\) 为前 \(i\) 个数分了 \(j\) 组的方案,答案即为 \(f_{n,c}\),转移显然,预处理前缀和并滚掉第二维,时间复杂度 \(\mathcal{O}(cn^2)\)。斜率优化可做到 \(\mathcal{O}(cn)\),但依旧无法通过。
实际上,这大部分这一类型的题目都有类似的 2D/1D dp 的做法。
1.2. 使用前提与凸性证明
能使用 wqs 二分的前提: 记 \(f(i)\) 为选取 \(i\) 个物品时的答案,\(f\) 是凸函数,且可快速计算极值和极值点。
证明函数的凸性方法很多,可以结合题目感性猜测其凸性,或是打表进行观察。
更多严谨的凸性证明见 OI Wiki,反正我看不懂 qaq。
2. 第一种理解
2.0. 核心思想
在讲算法之前,先给出它的核心思想:
- 令我们想求的值为 \(f(c)\),我们通过对 \(f\) 的解析式的处理形成一个新的函数 \(g\),使得新的函数 \(g\) 仍可快速计算极值和极值点,且其极值点恰为 \((c,g(c))\),再利用 \(f\) 和 \(g\) 的关系反推 \(f(c)\)。
听着很神奇,但先记好这个核心思想。
2.1. 算法流程
我们开始了,首先画出 \(f\) 的图像,显然它是一个离散的凸壳,接下来我们均考虑 \(f\) 下凸的情况:

此时我们要求可以快速求出其最小值点,记此时的最小值点为 \(m\)。如图,画出 \(f\) 的导函数图像 \(f'\),\((m,f'(m))\) 与 \((m+1,f'(m+1))\) 的连线与 \(x\) 轴有交)。\(f\) 下凸,\(f'\) 是增函数。

补充说明:
凸壳哪来的导数?
类比导数的定义,我们此处导数均指差分,即 \(f'(i)=\Delta_{i}=f(i)-f(i-1)\),那么此时取到最小值的点的差分为正,且它下一个数的差分为正,自己画图理解一下。但不管怎么样,肯定可以通过平移使得 \(g'(c)=0\)。二分的时候,由于我们是 dp 求极值和极值点的,所以无关紧要。
依照概述部分的核心思想,我们考虑如何构造这个 \(g\):首先我们希望最小值点落在 \(c\),怎么办呢?我们从 \(g'\) 入手。若希望最小值点落在 \(c\),需要 \((c,g'(c))\) 与 \((c+1,f'(c+1))\) 的连线与 \(x\) 轴有交,不妨令 \(g'(c)=0\)。我们让 \(f'\) 的图像在垂直方向上运动,由于它是增函数,感性理解,我们总能让它的零点落到 \(c\) 上,那这就是我们想要的 \(g'\)!

记垂直向上平移了 \(k\) 个单位长度,那么有 \(g'(i)=f'(i)+k\),容易想到 \(g(i)=f(i)+ki\) 是一个可能的构造。并且由于 \(g'\) 有 \(f'\) 平移得到,仍为增函数,所以这个 \(g\) 仍具有凸性!凸性是非常好的性质,加上 \(g(i)=f(i)+ki\) 的形式非常简单,所以一般也可同 \(f\) 一样快速计算极值和极值极值点。

怎么完成平移的操作呢?我们二分 \(k\)。对每次二分的 \(mid\),我们求出此时 \(g\) 的极值点 \(t\):
- 若 \(t\gt c\),\(f'\) 还需向上平移,\(k\) 要增大;
- 若 \(t\lt c\),\(f'\) 还需向下平移,\(k\) 要减小。
自己对着图像理解一下。
回到 DP 部分,当我们 check 的时候,我们需求出当前 \(g\) 的最小值和最小值点,不同的是,现在不限制物品个数了!我们无需物品个数的维度,拿概述部分的例子来说:
\(\text{eg.}\) 给定一数列 \(a_n\),要求将其按顺序分割为 \(c\) 组,使得每组的代价和最小,其中每组的代价定义为该组内所有数的和的平方。\(n,c\le10^5,a_i\ge1\)。
显然有 2D/1D 的 dp:设 \(f_{i,j}\) 为前 \(i\) 个数分了 \(j\) 组的方案,答案即为 \(f_{n,c}\),转移显然,预处理前缀和并滚掉第二维,时间复杂度 \(\mathcal{O}(cn^2)\)。斜率优化可做到 \(\mathcal{O}(cn)\),但依旧无法通过。
实际上,这大部分这一类型的题目都有类似的 2D/1D dp 的做法。
容易证明它是凸的,wqs 二分可以去掉第二维,交由二分来实现。于是这个 dp 从 2D/1D 变成 1D/1D 的,我们成功实现了降维!check 里面直接写这个 dp 即可,至于极值点,转移时记录上一个状态即可。
另外,这东西本就可以斜优,所以可以做到 \(O(n\log V)\),其中 \(V\) 是二分值域。
wqs 二分就讲完了……吗?
3. 特殊情况:三点共线
考虑下图中三点共线的情况,那么有 \(f'(c)=f'(b)\),然后你会惊讶的发现,无论如何平移都有 \(f'(c)+k=f'(b)+k\),即 \(a,c,b\) 三点始终共线,夹在中间的 \(c\) 不可能取到最小值,怎么都二分不到!

我们先二分出最小值点为 \(a\) 时的 \(g(a)\),解决方案是:\(f(c)=g(a)-kc\),为啥?先展开:
\]
即:
\]
这是点斜式!我们接下来只需证明 \(k_{AC}=-k\) 即可。
其实非常显然,\(f'\) 经过平移的到 \(g'\),且 \(g'(c)=0\),那么就向上平移了 \(k=-f'(c)=-k_{AC}\) 个单位长度,于是有 \(k_{AC}=-k\)。
怎么实现?找不到 \(c\) 时,我们二分出 \(c\) 右边可以取到的最接近 \(c\) 的点即可。
这个问题启发我们:\(k\) 貌似和凸壳上线段的斜率有一定联系,而我们知道凸壳上的斜率是单调的,不同斜率的直线在凸壳上的切点也是单调的,能不能利用这种性质来二分 \(k\) 呢?
所以不好意思——还没讲完。
4. 再谈算法
4.1. 第二种理解
实际上大家在网上看到的大部分博客都是这个做法,但我觉得这个做法其实比较难抓住它的动机。
依旧讨论下凸壳。
考虑一条与下凸壳相切的斜率为 \(k\) 直线 \(l_k\),记其 \(y\) 轴截距为 \(b(k)\),过凸壳上一点 \((p,f(p))\),那么有 \(f(p)=kp+b(k)\)。由凸壳相切的性质,我们必然可以找到一个 \(k\),使得与凸壳相切的 \(l_k\) 切到点 \((c,f(c))\),这时我们只需求出 \(b(k)\) 即可求出 \(f(c)\)。

由前文所述的凸壳优秀的切点单调性(随斜率单调变化),我们直接二分 \(k\),好好利用这个性质来定位 \((c,f(c))\)。对于每个二分出的斜率 \(mid\),我们求出切点的横坐标 \(p\),并用其与 \(c\) 的关系判断 \(k\) 与 \(mid\) 的大小关系:
- 若 \(p\lt c\),此时切点偏左,斜率偏小,\(k\gt mid\);
- 若 \(p\gt c\),此时切点偏右,斜率偏大,\(k\lt mid\)。
自己对着图像理解一下。
问题转换为求切点横坐标 \(p\)。由相切的性质,\(l\) 是所有过凸壳顶点且斜率为 \(k\) 的直线中,\(y\) 轴截距最小的。又 \(b=y-kx\),于是有 \(b(k)=\min\limits_{1\le p\le n}f(p)-kp\),且其对应的 \(p\) 即为切点横坐标。


哎,这不跟第一种理解方式中的 \(g(i)=f(i)+ki\) 一模一样吗!所以我们也可以快速 dp 求解这个式子,并同样通过记录上一个状态求出切点横坐标 \(p\)。
最后将二分的结果 \(k\) 和其对应的切点横坐标 \(c\) 和截距 \(b(k)\) 代回 \(f(p)=kp+b(k)\) 即可求出 \(f(c)\)。
4.2. 再论三点共线
返回来从这个角度考虑三点共线的情况。此时找不到 \(c\),考虑 \(c\) 右边可以取到的最接近 \(c\) 的点 \(a\)。由于 \(a\) 在 \(c\) 右边,所以我们二分出切点在 \(a\) 时的最小斜率,这对应的切线 \(l_k\) 即为直线 \(AC\)。

由于过 \(c\),此时的 \(k\) 和 \(b(k)\) 都使用与 \(c\),于是有 \(f(c)=kc+b(k)\)。
4.3. 更多细节
还没讲完,还有细节。
对三点共线情况的研究同样很有启发性。我们说 “二分出切点在 \(a\) 时的最小斜率”,可以发现,当我们二分的 \(k\) 恰为凸壳上相邻两个点的连线的斜率时,切线 \(\bm{l_k}\) 同时切到的时凸壳的一条边而非一个点,问题就来了:它同时切到了两个(及以上,多点共线时)点,那我们应认为它对应的是哪个切点呢?这关系到我们二分调整范围的过程!
这是 wqs 二分最烦容易出错的地方,关键在于要钦定一个偏序关系。比如说,前文我们需要 “二分出切点在 \(a\) 时的最小斜率”,我们就可以将这条线的斜率的贡献算到(最)靠右的点内,同时在 dp 值相等时取选择物品个数更大的那个。不管怎样,写二分的时候想好范围如何更新。
另外,大部分题目的 \(f\) 为整数,所以相邻两点连线的斜率/导函数(差分)也为整数,在钦定偏序关系后必然可以二分到整数 \(k\)。对于需要实数二分的题目,由于每个点对应的斜率时一个区间,所以设定好精度,谨防 TLE。
5. 例题
由于笔者涉猎不深,仅能给出一些经典例题了。
6. 参考资料
学习笔记/DP:wqs 二分概述的更多相关文章
- 「学习笔记」wqs二分/dp凸优化
[学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...
- ArcGIS API for JavaScript 4.2学习笔记[0] AJS4.2概述、新特性、未来产品线计划与AJS笔记目录
放着好好的成熟的AJS 3.19不学,为什么要去碰乳臭未干的AJS 4.2? 4.2全线基础学习请点击[直达] 4.3及更高版本的补充学习请关注我的博客. ArcGIS API for JavaScr ...
- IOS学习笔记02---语言发展概述,计算机语言简介.
IOS学习笔记02---语言发展概述,计算机语言简介. ------------------------------------------------------------------------ ...
- Java NIO 学习笔记(一)----概述,Channel/Buffer
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- ZooKeeper学习笔记(一)——概述
zookeeper学习笔记(一)--概述 1. 概述 Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目.zookeeper从设计模式的角度来理解:是一个基于观察者设计 ...
- [原创]java WEB学习笔记51:国际化 概述,API 之 locale类,dataFormat类,numberFormat类, MessageFormat类,ResourceBundle 类
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- CF739E Gosha is hunting DP+wqs二分
我是从其他博客里看到这题的,上面说做法是wqs二分套wqs二分?但是我好懒呀,只用了一个wqs二分,于是\(O(nlog^2n)\)→\(O(n^2logn)\) 首先我们有一个\(O(n^3)\)的 ...
- [九省联考2018]林克卡特树(DP+wqs二分)
对于k=0和k=1的点,可以直接求树的直径. 然后对于60分,有一个重要的转化:就是求在树中找出k+1条点不相交的链后的最大连续边权和. 这个DP就好.$O(nk^2)$ 然后我们完全不可以想到,将b ...
- Direct2D 学习笔记(1)概述
Direct2D 应用程序接口概述 资源网站 https://docs.microsoft.com/en-us/windows/win32/Direct2D/the-direct2d-api 主要用到 ...
- P4383 [八省联考2018]林克卡特树 树形dp Wqs二分
LINK:林克卡特树 作为树形dp 这道题已经属于不容易的级别了. 套上了Wqs二分 (反而更简单了 大雾 容易想到还是对树进行联通情况的dp 然后最后结果总和为各个联通块内的直径. \(f_{i,j ...
随机推荐
- 【经验】Python3|输入多个整数(map方法或ctypes调用C标准库scanf)
文章目录 方法一:多次调用input 1. 代码 方法二:调用C标准库 1. 代码 2. 残留的问题(int数组取元素) 附:计算时间差的程序(使用实例) 第一种读取方式: 第二种读取输入方式: 方法 ...
- 百图生科:基于 JuiceFS 构建生命科学大模型存储平台,成本降 90%
百图生科(BioMap)由百度创始人李彦宏先生联合创立,专注于生命科学领域的人工智能技术.公司推出了全球最大的生命科学 AI 基础模型 xTrimo V3,拥有 2100 亿参数,覆盖蛋白质.DNA. ...
- Python 变量作用域 LEGB
回顾 - Decorator 前篇有讲到了, 闭包和装饰器的概念. 闭包就是, 函数内部嵌套函数. 而 装饰器只是闭包的特殊场景而已, 特殊在如果外函数的参数是指向一个, 用来被装饰的函数地址时(不一 ...
- 图解Spring源码4-Spring Bean的作用域
>>>点击去看B站配套视频<<< 系列文章目录和关于我 1. 从一个例子开始 小陈经过开店标准化审计流程后,终于拥有了一家自己的咖啡店,在营业前它向总部的咖啡杯生产 ...
- 使用torch pruning工具进行结构化剪枝
网络结构定义 import torch import torch.nn as nn import torch.nn.functional as F import torch_pruning as tp ...
- @FeignClient注解自定义接口超时时间
问题描述 每个微服务都有统一的接口超时时间设定,但也存在一些特殊的业务场景,其接口需要较长的超时时间,比如:导出excel报表.上传文件.拉取业务报表数据等等.此时,默认的超时设置就不能满足需求, ...
- X-MACRO使用技巧
背景 最近遇到一个问题,需要将分区表硬编码在代码,第一反应可能是定义个数组,数组内容包括分区名称和分区大小. 类似于这种: struct Partition { const char *name; i ...
- 洛谷 P6626 [省选联考 2020 B 卷] 消息传递
洛谷 P6626 [省选联考 2020 B 卷] 消息传递 Problem 原题传送门 给一棵有\(n\)个节点的树.有\(m\)个询问,每次给出一对\(x,k\)表示查询到点\(x\)的距离为\(k ...
- CC爬虫攻击测试与防护
CC爬虫攻击测试与防护 本文章旨在对最基本的CC攻击进行测试与防护,本次测试的所有站点均为本人自建,没有也不会去攻击其他站点.希望各位读者能够遵循当地法律法规,不要做危害他人计算机的行为 测试流程 裸 ...
- TUF系统概述
TUF基本介绍 TUF 是一个为软件更新系统设计的安全框架,最初由纽约大学的 Secure Systems Lab 提出.它的目标是解决传统软件更新过程中的各种安全问题(如中间人攻击.回滚攻击.密钥泄 ...