【二分查找】
【二分查找】
二分模版
如果二分出来有问题,一定不会是模版的问题!
/*
若R=mid L=mid+1->找满足条件更小的->(L+R)>>1
若L=mid R=mid-1->找满足条件更大的->(L+R+1)>>1
答案就是L/R
*/
int L=0,R=INF;
while(L<R){
int mid=(L+R)>>1;
if(check(mid)) R=mid;
else L=mid+1;
}
int ans=L;
注意l和r的边界问题!!!
STL中的二分查找
只要是有序序列都可以使用:vector、set、multiset
//首个*大于等于*给定值的元素的函数
lower_bound(first,last,val)
//首个*大于*给定值的元素的函数
upper_bound(first,last,val)
//一般都是返回位置 需要加*
//如果需要找小于/小于等于的最大?传cmp即可
bool cmp(const int &a,const int &b) { return a > b; }
int *p1=lower_bound(a+1,a+n+1,k,cmp);
题目积累
Skibidus and Fanum Tax (hard version)
https://codeforces.com/contest/2065/problem/C2
思路
遍历a数组,依次确定每个数
在b数组里二分找大于前一个已经确定的数的b[j]-a[i](记得先排序)
判无解:
(1)找不到b->b边界到最右边(注意手写二分时的边界问题)
(2)a[i]也小于res
特别的:a[1]要尽量小->b[1]也要尽量小
代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=2e5+10;
const ll INF=0x3f3f3f3f;
int t;
int n,m;
ll a[N],b[N];
void solve(){
cin>>n>>m;
memset(a,0,sizeof a);
memset(b,0,sizeof b);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
sort(b+1,b+1+m);
if(n==1) cout<<"YES"<<endl;
else{
ll res=0;
res=min_(a[1],b[1]-a[1]);
bool is_ok=true;
for(int i=2;i<=n;i++){
//二分判无解:坐标在第一个/最后一个(注意l和r的区间!!!)
int l=1,r=m+1;
while(l<r){
int mid=(l+r)>>1;
if((b[mid]-a[i])>=res) r=mid;//二分一个修改后大于等于某数的最小值
else l=mid+1;
}
ll tmp=INF;
if(l!=m+1) tmp=min_(tmp,b[l]-a[i]);
if(a[i]>=res) tmp=min_(tmp,a[i]);
if(tmp==INF){
is_ok=false;
break;
}
res=tmp;
}
if(is_ok) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
Circle Perimeter
【边界问题】什么情况是找不到的?
https://codeforces.com/contest/1971/problem/F
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=1e5+10;
ll x;
void solve(){
cin>>x;
ll ans=0;
//找左右边界即可 角点单独判断:注意边界问题
for(ll i=1;i<=x;i++){
//找左端点
//什么情况是找不到的?找到的点>=i
ll l=1,r=N;
while(l<r){
ll mid=(l+r)>>1;
ll tmp=(i*i+mid*mid);
if(tmp>=x*x) r=mid;
else l=mid+1;
}
ll cnt1=l;
//cout<<cnt1<<" ";
if(cnt1<i && cnt1>=1){
l=1,r=N;
while(l<r){
ll mid=(l+r+1)>>1;
ll tmp=(i*i+mid*mid);
if(tmp<(x+1)*(x+1)) l=mid;
else r=mid-1;
}
ll cnt2=l;
//cout<<cnt2;
if((i*i+cnt2*cnt2)>=x*x){
if(cnt2<1) continue;
else if(cnt2>=i) cnt2=i-1;
ll res=cnt2-cnt1+1;
ans+=res;
}
}
}
ans*=8;
ans+=4;
ll L=0,R=x;
while(L<R){
ll mid=(L+R)>>1;
if(mid*mid*2>=x*x) R=mid;
else L=mid+1;
}
if(L*L*2<(x+1)*(x+1) && L*L*2>=x*x) ans+=4;
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--) solve();
return 0;
}