二分学习笔记
题面传送门
算法简介:二分查找(答案),时间复杂度\(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;
}