震惊,我居然忘记了二分
这件事真恐怖
好了我知道我很菜
二分能解决的问题很简单,仅仅三种
- 存在单调性的查找
- 最大最小值(使……最大的最小值)
- 最小最大值(同理)
二分的结构
不说了直接代码
while(l<=r)//在可能的范围内
{
if(check()) update(ans),change(l);//如果符合题意,更新答案并且优化子集搜索区间
else change(r);//如果不符合题意,弱化子集搜索区间
}
好像说得不清楚
子集即为答案可能存在的集合,优化即为向更优的方向改变答案,弱化即为由于不符合题意而被迫寻找更次的答案(我知道我语文不好)
二分的实质即为不断缩小答案或最优解所在集合最后确定解或最优解
二分的功能和性质
- 从代码上看,二分始终在维护更优答案所在的区间,但始终是一个区间。也就是说,它只能处理仅有一个合法峰值的问题。在实际使用的时候,我们要注意存在\(x_m\)且 $ f(x_m)=max\lbrace f(x)\rbrace$。
- 大部分二分都题目中数据都存在单调性。即当数据为单调递增时,很可能存在某个\(x\)满足\(f(x)\)优于\(f(x-1)\)且\(f(x+1)\) 不存在。
check函数的写法
又是一堆代码
bool check(int x)//传可能的答案进来模拟,即假设答案为x
{
...//由题意进行,检查实际操作时是否与题意有冲突
if(flag==0) return 0;//不符合题意,这个可以加在上一个过程中,可以节约时间
return 1;//符合题意
}
在返回值的时候,请注意与下方答案更新项相匹配
对于二分最容易错的符号问题
其实跳石头我都不会,这真是令人窒息的操作
但实际上很简单
1.对于while后面是\(l<=r\)还是\(l<r\)的问题
实际上就是l=r时是否进行的问题,显然还是要进行的,因为此时答案可能存在的区间还有一个元素,所以一定是\(l<=r\)
2.对于下方l,r是否加一减一的操作
实际上就是答案子集区间是否可能存在边界即为答案的可能性。对于符合题意时,由于在r和l中一定有更新答案这一步,所以更新的答案一定是符合题意的,更优的答案自然不会包括已经更新的答案;而在不符合题意的时候,答案更优解就更不可能为不符合题意的解了,即同样不会包括不合法的解。
总之,l<=r,l=mid+1,r=mid-1
实战
给定长度为 n 的数组 A 和 B,将数组 A 和 B 数组中的元素两两相乘,得到长度为 n ∗ n 的数组 C,求 C 中第 K 大数。
数据范围 n ≤ 50000, ai, bi ≤ \(10^9\)
首先我们不可能把50000*50000的全部求出来遍历,那我们可以想啊,二分不是可以假设答案吗?于是我们假设m为第K大的数,它显然符合二分的原则。那么如何验证呢?我们对于每一个\(a_i\),对排序后的\(b_i\)再次二分求乘积就知道有多少数比m小了。复杂度\(O(n\log n\log10^{18})\)