数学与数论|二分
1.基础二分
2.双指针
心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。
基础二分
模板
bool check(int x){/*...*/} //检查是否满足某种性质
//区间[l,r]被划分成[l,mid]和[mid+1,r]时使用:
int bsearch_1(int l,int r){
while(l<r){
int mid = l+r>>1;
if(check(mid)) r=mid;//check()判断mid是否满足性质
else l=mid+1;
}
return l;
}
//区间[l,r]被划分为[l,mid-1]和[mid,r]时使用:
int bsearch_2(int l,int r){
while(l<r){
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
return l;
}
题目描述
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1∼10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
实践代码:
void solve(){
int n,q;cin>>n>>q;
vector<int> a(n);
for(int i=0;i<n;i++) cin>>a[i];
while(q--){
int k;cin>>k;
int l=0,r=n-1;
while(l<r){//从右向左找到起始位置
int mid=l+r>>1;
if(a[mid]>=k) r=mid;//check
else l =mid+1;
}
if(a[l]!=k) {cout<<"-1 -1"<<endl;continue;}//没找到
if(a[l]==k) cout << l << ' ';
while (l < r) {//从左向右找到终止位置
int mid = l+r+1>>1;
if (a[mid]<=k) l=mid;//check
else r =mid-1;
}
cout << l << endl;
}
}
双指针
双指针:两个变量模拟指针的功能在数组间移动
1.两个指针之间的区间1具备某种特征
2.两个指针所指的东西要满足一些条件
题目描述
给定一个长度为n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含三个整数n。
第二行包含n个整数(均在0~105范围内),表示整数序列。
输出格式
共1行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1 ≤ n ≤ 105
输入样例
5
1 2 2 3 5
输出样例
3
实践代码:
void solve(){
int n;cin>>n;
int ans=0;
vector<int> a(n+1);
map<int,int> mp;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0,j=0;i<n;i++){
mp[a[i]]++;
while(mp[a[i]]>1) {mp[a[j]]--;j++;}
ans=max(ans,i-j);
}
cout<<ans;
}
例题 (双指针+二分)
题目描述
给定一个长度为n的数组a和一个整数m,求其中有多少对二元组(ai,aj)满足:
1 ≤ i ≤ j ≤ n 且 ai+aj > m
输入格式
第一行两个整数n,m,意义如上。(1≤n≤2x105,1≤m≤109)
第二行n个整数,第i个整数表示ai。(0≤ai≤109)
输出格式
一个数字,表示满足条件的二元组的个数。
输入样例
53 5
2 3 4
输出样例
32
注意:
设i,j为双指针,定右端点j,j每次向右+1,i在小于j的区间二分找a[i]+a[j]>m的第一个下标i
补充:
lower_bound()/upper_bound()
:
/*lower_bound()/upper_bound()
*在已升序排序的元素中,应用二分查找检索指定元素,返回对应元素迭代器位置,找不到则返回尾迭代器
* lower_bound():寻找>=x的第一个元素的位置
* upper_bound():寻找>x的第一个元素的位置
* 怎么找<=x/<x的第一个元素呢?
* >x的第一个元素的前一个元素(如果有)便是<=x的第一个元素
* >=x的第一个元素的前一个元素(如果有)便是<x的第一个元素
* 返回的是迭代器,如何转成下标索引呢?减去头迭代器即可。
* */
cout<<"lower_bound()/upper_bound():"<<endl;
vector<int> arr4{0,1,1,1,8,9,9};
//找下标(位置)
int pos1 = lower_bound(arr4.begin(),arr4.end(),8)-arr4.begin();
cout<<pos1<<endl;
int pos2 = upper_bound(arr4.begin(),arr4.end(),8)-arr4.begin();
cout<<pos2<<endl;
//找不到返回尾迭代器
vector<int>::iterator it = lower_bound(arr4.begin(),arr4.end(),99);
int idx = it - arr4.begin();
cout<<idx<<endl;
int pos3= upper_bound(arr4.begin(),arr4.end(),99)-arr4.begin();
if(pos3==arr4.size()){
cout<<"No Found!"<<endl;
}
实践代码:
void solve(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
int ans=0;
for(int j=1;j<=n;j++){
int i= upper_bound(a+1,a+j,m-a[j])-a;//底层是二分,找到小于j且a[i]>m-a[j]的第一个下标i
ans+=j-i;
}
cout<<ans<<endl;
}
心有猛虎,细嗅蔷薇。再见了朋友~