题目描述
蒜头君手上有个长度为 nn 的数组 AA。由于数组实在太大了,所以蒜头君也不知道数组里面有什么数字,所以蒜头君会经常询问在数组 AA 中,等于 xx 的数字有多少个?
输入格式
第一行输入两个整数 nn 和 mm,分别表示数组的长度和查询的次数。
接下来一行有 n个整数
接下来 m 行,每行有 1个整数 x,表示蒜头君询问的整数。
输出格式
对于每次查询,输出一个整数,表示数组 A 中有多少个 x。
数据范围
1<=n,m<=1e5,0<=x<=1e6
样例输入
10 5
1 1 1 2 3 5 5 7 8 9
0
1
4
9
10
样例输出
0
3
0
1
0
代码1(手写二分)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int search1(int a[],int x,int l, int r) //左边等于x的第一个数
{
while(l<r)
{
int mid=l+r>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
return l;
}
int search2(int a[],int x, int l, int r ) //右边等于x的第一个数
{
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]<=x) l=mid;
else r=mid-1;
}
return r;
}
int main()
{
int n,m;
scanf("%d",&n);
int a[n];
scanf("%d",&m);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
while(m--)
{
int h;
scanf("%d",&h);
int l=search1(a,h,0,n-1);
int r=search2(a,h,0,n-1);
if(a[l]==h&&r-l>=0)//如果查找到的数检验确实是等于要找的数 输出r-l+1
cout<<r-l+1<<endl;
else//否则就是查找到的数检验不是要找的数 输出0
cout<<0<<endl;
}
return 0;
}
代码2(STL)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> v;
int main()
{
int n,m,x;
cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>x;
v.push_back(x);
}
sort(v.begin(),v.end());
while(m--)
{
cin>>x;
vector<int> :: iterator it1=lower_bound(v.begin(),v.end(),x);//第一个等于x的数
vector<int>:: iterator it2=upper_bound(v.begin(),v.end(),x);//第一个大于x的数
if(it1==v.end()||*it1!=x)
cout<<0<<endl;
else
cout<<it2-it1<<endl;
}
return 0;
}
1.手写二分模板时候一定要清楚控制条件怎么输入,那个是求左边界的数,哪个是求有边界的数:
2.这里我的想法就是 如果控制条件里有“=”,则我所写的第一个就是左边第一个等于x的数,第二个就是右边最后一个等于x的数
3.如果控制条件里不加等号,直接判断外围边界,那么第一个模板就变成了右边第一个大于x的数,第二个模板就变成了左边最后一个小于x的数,如果晕的话,可以用这个题目来试试。
实验代码1
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int search1(int a[],int x,int l, int r) //左边等于x的第一个数
{
while(l<r)
{
int mid=l+r>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
return l;
}
int search2(int a[],int x, int l, int r ) //右边等于x的第一个数
{
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]<=x) l=mid;
else r=mid-1;
}
return r;
}
int main()
{
int n,m;
scanf("%d",&n);
int a[n];
scanf("%d",&m);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
while(m--)
{
int h;
scanf("%d",&h);
int l=search1(a,h,0,n-1);
int r=search2(a,h,0,n-1);
cout<<l<<" "<<r<<endl;//输出发生了改变,其余没动
}
return 0;
}
实验代码2
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int search1(int a[],int x,int l, int r) //左边等于x的第一个数
{
while(l<r)
{
int mid=l+r>>1;
if(a[mid]>x) r=mid;//去掉了等号
else l=mid+1;
}
return l;
}
int search2(int a[],int x, int l, int r ) //右边等于x的第一个数
{
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]<x) l=mid;//去掉了等号
else r=mid-1;
}
return r;
}
int main()
{
int n,m;
scanf("%d",&n);
int a[n];
scanf("%d",&m);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
while(m--)
{
int h;
scanf("%d",&h);
int l=search1(a,h,0,n-1);
int r=search2(a,h,0,n-1);
cout<<l<<" "<<r<<endl;
}
return 0;
}
并且我们可以看到 在去掉等号寻找小于x的最后一个数和大于x的第一个数的过程中,出现在首位 和末尾的数字在判断左边界和右边界的时候只能定在首位和尾位