蒟蒻又滑了好多天。。。。。。。。。
愧疚。。。。。
题目来源:Acwing
题目链接1
题目链接2
这是一个整数二分很好的题:
给定一个按照升序排列的长度为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
首先 这个数组是 单调!(全部单调或者局部单调的题都可以使用二分)
也就是连续的 相当于找一找数的左边界和右边界
#include<iostream>
using namespace std;
const int N=100010;
int a[N];
int main(){
int n,d;
cin>>n>>d;
for(int i=0;i<n;i++) cin>>a[i];
while(d--){
int k;
cin>>k;
int l=0,r=n-1;
while(l<r){// 这里找的是左边界
int mid=l+r>>1;
if(a[mid]>=k) r=mid;//大了就把右边界往左缩
else l=mid+1;//小了就把左边界往右缩
}
if(a[l]!=k) cout<<"-1 -1"<<endl;
else{
cout<<l<<' ';
int l=0,r=n-1;
while(l<r){// 这里找的是右边界
int mid=l+r+1>>1;// 加一是为了保证不死循环
if(a[mid]<=k) l=mid;//小了就把左边界往右缩
else r=mid-1;//大了就把右边界往左缩
}
cout<<l<<endl;
}
}
return 0;
}
这样写只需要120ms左右嗷 如果换成scanf/printf的话只需要40ms左右
假如我们用暴力的做法
#include<iostream>
using namespace std;
const int N=100010;
int a[N];
int main(){
int n,q;
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
while(q--){
int num;
scanf("%d",&num);
bool total=false;
for(int i=0;i<n;i++){
if(a[i]==num&&total==0){ printf("%d ",i); total=true; break;}
}
if(total) for(int i=n-1;i>=0;i--){
if(total&&a[i]==num){
printf("%d\n",i);
break;
}
}
if(!total) printf("-1 -1\n");
}
return 0;
}
就会得到TLEEEEEEEEEEEEEEE的结果
然后俺们来看一个 浮点数二分的题:
给定一个浮点数n,求它的三次方根。
输入格式 共一行,包含一个浮点数n。
输出格式 共一行,包含一个浮点数,表示问题的解。
注意,结果保留6位小数。
数据范围 −10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000
我们可以从令左边界为-10000,右边界为10000,然后互相趋近
#include<iostream>
using namespace std;
double xpp(double x){
return x*x*x;
}
int main(){
double a;
scanf("%lf",&a);
double l=-10000,r=10000;
while(r-l>=1e-8){//一般取到精度的下两位
double mid=(l+r)/2;
if(xpp(mid)>=a) r=mid;//大了就把右区间往回拉
else l=mid;//小了就把左区间往右拉
}
printf("%.6lf",l);
return 0;
}
当然喜欢不喜欢printf的也可以用cout配合iomanip里的setprcision fixed来取精度^^
然后还有种快速的做法 就是用cmath里的cbrt函数(排位神器),可以直接开三次方,然后再取个6位小数就可以啦
因为数据不大 时间差距不大 想怎么写就怎么写啦!