二分的两个模板
用途其实不能相互代替
一个只能求>(=)一个只能求<(=)
所以设计二分的时候要清楚
在要寻找的数组 单调递增的时候
1、选择mid=l+r>>1还是l+r+1>>1
前者只能求>(=)a 后者只能求<(=)a
2、初始状态,若是mid=l+r>>1,那么就只要考虑r的状态要不要是开区间。
相反,若是mid=l+r+1>>1,只要考虑l的状态是否是开区间
单调递减的状态如下图
最长上升序列
O(nlogn)
传送门
维护一个单增的q数组
里面有所有最长上升序列
然后不断读取a数组,并在q中二分查找<a的数
如果a数组能使得q数组更优(即替换里面某个数使其更小或是能加在末尾)
那么替换/增加
然后更新len和q数组
注意此处二分在递增序列中查找<a的数
显然选择mid=l+r+1
然后考虑边界
只需要考虑r
如果找不到<a的数的话,返回len,因为这样和后面r+1匹配兼容
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],q[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int len=0;
for(int i=1;i<=n;i++){
int l=0,r=len;
while(l<r){
int mid=l+r+1>>1;
if(a[i]>q[mid])l=mid;
else r=mid-1;
}
len=max(len,r+1);
q[r+1]=a[i];
}
cout<<len;
return 0;
}
如果用mid=l+r>>1也可以
只不过要分情况讨论
可以把l和r都定为开区间
然后分三种情况讨论就可以解决所有二分问题!
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],q[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int len=0;
// q[0]=-1e9;
for(int i=1;i<=n;i++){
int l=0,r=len+1;
while(l<r){
int mid=l+r>>1;
if(a[i]<=q[mid])r=mid;
else l=mid+1;
}
if(r==len+1){
len=r;
q[r]=a[i];
}else if(r==0){
len=max(len,1);
q[1]=a[i];
}else {
// len=max(r,len);
q[r]=a[i];
}
//q[r-1]=a[i];
}
cout<<len;
return 0;
}