二分

最近碰到的二分题有点多,而且方法都不同,拿来说一下,顺便给自己做下总结

模板1:

    while (l < r)
    {
        int mid = l + r >> 1;    //(l+r)/2
        if (check(mid))  r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }

模板2:

    while (l < r)
    {
        int mid = l + r + 1 >> 1;    //(l+r+1)/2
        if (check(mid))  l = mid;
        else r = mid - 1;
    }

第一个模板是尽量往左找目标,第二个模板是尽量往右找目标。

只要是往左找答案,就用第一个模板,mid不用加一,r=mid,l加一;
只要是往右找答案,就用第二个模板,mid要加一,l=mid,r要减一;

浮点二分:

 

    while(r-l>1e-5) //需要一个精度保证
    {
        double mid = (l+r)/2;
        if(check(mid)) l=mid; //或r=mid;
        else r=mid; //或l=mid;
    }

 

 

 

二分查找与普通二分答案:一般是套用第二个模板,最终得到的L就是最终答案:
最小值最大,第二个模板,最大值最小,第一个模板

 额外补充一点,二分题很多都要开到long long所以这一点一定要注意

 

//木材加工:https://www.luogu.com.cn/problem/P2440
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
long long n,k,a[N],res,l,r;
bool check(int u)
{
    int num=0;
    for(int i=0;i<n;i++) num+=(a[i]/u);
    if(num>=k) return true;
    else return false;
}
int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++) cin>>a[i],r=max(r,a[i]);
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l;
    return 0;
}

 

//Primes on Interval: https://www.luogu.com.cn/problem/CF237C
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,k,res,num,p[N],prime[N];
bool vis[N];
bool check(int u)//滑动窗口判断素数
{
    int hh=n-1,tt=n,cnt=0;//建立队头队尾
    while(hh-tt+1<u){//找到区间的第一个头
        hh++;
        if(!vis[hh]) cnt++;//先++;
    }
    while(hh<=m){
        if(cnt<k) return false;//判断
        hh++; if(!vis[hh]) cnt++;//向前滑动一位,并判断
        tt++; if(!vis[tt-1]) cnt--;//队尾也向前,如果划掉素数了就要减
    }
    return true;
}
int main()
{
    cin>>n>>m>>k;
    vis[1]=true;
    for(int i=2;i<=m;i++){
        if(!vis[i]) prime[++num]=i,p[i]=i;
        for(int j=1;prime[j]<=m/i;j++){
            vis[i*prime[j]]=true;
            if(!(i%prime[j])) break;
        }
    }
    if(!check(m-n+1)) return cout<<-1,0;
    int l=1,r=m+1;
    while(l<r){//模板一,找小值
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<<r;
    return 0;
}

 

//借教室:https://www.luogu.com.cn/problem/P1083
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
long long c[N],need[N],st[N],ed[N],n,m,res,a[N],p[N];
bool check(int u)
{
    memset(c,0,sizeof c);
    for(int i=1;i<=u;i++) c[st[i]]+=need[i],c[ed[i]+1]-=need[i];
    for(int i=1;i<=n;i++){
        p[i]=p[i-1]+c[i];
        if(p[i]>a[i]) return false;
    }
    return true;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++) cin>>need[i]>>st[i]>>ed[i];
    if(check(m)) return cout<<0,0;
    int l=1,r=m;
    //二分最左端
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) l=mid+1;
        else r=mid;
    }
    cout<<-1<<endl<<l;
    return 0;
}
//Present:https://www.luogu.com.cn/problem/CF460C
//最小值最大
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int n,m,w;
ll res,l=2e9,r=2e9,a[N],c[N];
bool check(int h)
{
    ll day=0,now=0;
    for(int i=1;i<=n;i++){
        now+=c[i];
        if(now<h){
            day+=h-now;
            c[i]+=h-now;
            if(i+w<=n) c[i+w]-=h-now;
            now+=h-now;
        }
    }
    return day<=m;
}
int main()
{
    cin>>n>>m>>w;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        l=min(l,a[i]);
        r=max(r,a[i]);
        c[i]=a[i]-a[i-1];
    }
    //模板二
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
        for(int i=1;i<=n;i++) c[i]=a[i]-a[i-1];
    }
    cout<<l<<endl;
    return 0;
}

 

//[COCI 2011/2012 #5] EKO / 砍树:https://www.luogu.com.cn/problem/P1873
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
long long n,m,res,a[N],l,r;
bool check(int u)
{
    long long num=0;
    for(int i=0;i<n;i++) if(a[i]>u) num+=a[i]-u;
    return num>=m;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>a[i],r=max(r,a[i]);
    while(l<r){
       long long mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l;
    return 0;
}

 

//最大化最小值
//[NOIP2015 提高组] 跳石头:https://www.luogu.com.cn/problem/P2678
#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N=1e5+10;
ll n,m,st,a[N],res,l,r,p[N];
bool check(int u)
{
    ll num=0;
    for(int i=0;i<=n+1;i++) p[i]=a[i];
    for(int i=1;i<=n+1;i++) if(p[i]-p[i-1]<u) p[i]=p[i-1],num++;
    return num<=m;
}
int main()
{
    cin>>st>>n>>m;
    l=0,r=st,a[0]=0,a[n+1]=st;
    for(int i=1;i<=n;i++) cin>>a[i];
    while(l<r){
        ll mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l;
    return 0;
}

 

//路标放置:https://www.luogu.com.cn/problem/P3853
//最大值最小,模板一
//难点在于check函数
//来自大佬的题解:
//我们可以想象,我们已知了这条路上的所有的路标,我们从头开始枚举两两相邻的路标的间距.
//如果大于G,那么已经不符合G为最大距离的条件了,为了使G满足,我们就可以在前一个路标前面G米处加一个路标,这样与前面一个就符合条件了.
//再判断新设的路标和后面的路标是否距离小于G,如果不,继续重复操作设置新路标
//当新设的路标数已经超过题目所给最大增设值时,如果还有路标不满足G,但已经不能设置新路标了,所以该G值就不满足条件。相反,则G成立。
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10;
int a[N],res,n,m,k,l,r,op;
bool vis[N];
bool check(int u)
{
    int num=m,now=0;
    for(int i=0;i<=op;i++){
        if(num<0) break;
        if(a[i]-now<=u) now=a[i];
        else{
            now+=u;
            i--;
            num--;
        }
    }
    return num>=0;
}
int main()
{
    cin>>op>>n>>m;
    for(int i=0;i<n;i++) cin>>a[i],r=max(r,a[i]-a[i-1]),vis[a[i]]=true;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<<r;
    return 0;
}

浮点数二分:

 

//浮点数二分:
//不会做,纯纯的理论数学,不会搞公式
//银行贷款:https://www.luogu.com.cn/problem/P1163
#include<bits/stdc++.h>
#define EPS 1e-9
using namespace std;
const int N=1e5+10;
double now,next1,need,l,r,res;
bool check(double u)
{
    return (pow(1.0/(1.0+u),need)>=1-now/next1*u);
}
int main()
{
    cin>>now>>next1>>need;
    l=0,r=10;
    while(r-l>=EPS){
        double mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%.1lf",l*100);
    return 0;
}

 

//浮点数二分
//kotori的设备:https://www.luogu.com.cn/problem/P3743
#include<bits/stdc++.h>
#define EPS 1e-5
using namespace std;
const int N=6e5+10;
long double res,l,r,a[N],b[N],n,m;
bool check(long double u)
{
    long double sum=u*m,tmp=0;
    for(int i=0;i<n;i++)
        if(b[i]/a[i]-u<EPS) tmp+=(u-b[i]/a[i])*a[i];
    if(tmp-sum<=EPS) return true;
    else return false; 
}
int main()
{
    cin>>n>>m;
    double asum=0.0;
    for(int i=0;i<n;i++){
        cin>>a[i]>>b[i];
        asum+=a[i];
    }
    if(asum<=m)  return cout<<-1,0;//这一步很关键
    l=0.000,r=1e10;
    while(r-l>EPS){
        long double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    cout<<l;
    return 0;
}

 

posted @ 2023-06-17 13:59  o-Sakurajimamai-o  阅读(78)  评论(0)    收藏  举报
-- --