把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

二分学习笔记

题面传送门
算法简介:二分查找(答案),时间复杂度\(O(log^2n)\).可以在一个有序表中快速查找某个数。可以利用这个性质来二分答案来做到快速有效枚举。
算法实现:对于一个有序表,二分查找一个数是否存在。
首先定义边界值:\(l=0\),\(r=max(a_{i})+1\);然后取中值\(mid=\frac{(l+r)}{2}\)
这是一个有序表,满足\(a_{i-1}\leq a_i\leq a_{i+1}\)\(a_{i-1}\geq a_i\geq a_{i+1}\)
判断中点是否大于待查找数。若大于,则收拢左边界。反之则收拢右边界。则一定能找到待查找值是否存在。
复杂度:\(log^2(max(a_i)+1)\)
个人理解:挺有用的,但有局限性,必须在有序表中查询。
代码实现:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,a[100039],x,y,flag,l,r,mid;
inline void read(int &x){
    char s=getchar(); int f=1;x=0;
    while(s<'0'||s>'9'){if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
    x*=f;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) read(a[i]);
    sort(a+1,a+n+1);
    while(m--){
        flag=0; 
        read(x);
        l=1;r=n;
        while(l<r){
            mid=(l+r)>>1;
            if(a[mid]==x||a[l]==x||a[r]==x){printf("YES\n");flag=1;break;}
            if(a[mid]>x) r=mid-1;
            else if(a[mid]<x) l=mid+1;
        }
        if(!flag) printf("NO\n");
    }
    return 0;
}

二分应用:
\(1\):二分答案基础题(好吧第一次打这道题用了一个小时):
对于这道题,我们用\(data_i\)表示每小段长度为\(i\)时能切割出的小段数。
\(data_i\)可以表示为\(\sum\limits_{k=1}^{n}{\left\lfloor\dfrac{a_k}{i}\right\rfloor }\) 则当\(i<j<k\)时,\(data_i\geq data_j\geq data_k\)。满足二分答案的性质。
所以可以直接二分然后验证。对于答案小于\(m\),收拢右边界,反之收拢左边界。
代码实现:

#include<cstdio>
using namespace std;
int n,m,a[100039],l,r,mid,ans,flag;
int main(){
    register int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++) scanf("%d",&a[i]);
    l=0;r=100000001;
    while(l+1<r){
        mid=(l+r)>>1;
        ans=0;
        for(i=1;i<=n;i++) ans+=a[i]/mid;
        if(ans<m) r=mid;
        else l=mid; 
    }
    printf("%d",l);
}

\(2\):二分答案普及题(好吧第一次打这道题用了\(n\)个小时,\(n>2\)):
我们发现,当\(i<j\)时,当i订单不满足要求时,j订单一定不满足要求。所以这是一个二分找\(01\)交接点的问题。
当我们二分答案时可以借助差分数组验证。
代码实现:

#include<bits/stdc++.h>
using namespace  std;
int n,m,s[1000001],d[1000001],q[1000001],a[1000001],b[1000001],c[1000001],l,r,mid,flag;
int main() {
    //freopen("1.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++) scanf("%d",&s[i]);
    for(register int i=1;i<=m;i++)scanf("%d%d%d",&a[i],&b[i],&c[i]);
    l=0,r=m+1;
    while(l+1<r){
        flag=0;
        memset(d,0,sizeof(d));
        mid=(l+r)>>1;
        for(register int i=1;i<=mid;i++) d[b[i]]+=a[i],d[c[i]+1]-=a[i];
        for(register int i=1;i<=n;i++){
            d[i]+=d[i-1];
            if(d[i]>s[i]){flag=1;break;}
        }
        if(flag) r=mid;
        else l=mid;
    }
    if(l==m) printf("0");
    else printf("-1\n%d",r);
    return 0;
}
posted @ 2020-03-18 11:37  275307894a  阅读(52)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end