二分(答案)
什么情况下用二分?两个条件:上下界[a, b]确定、函数在[a, b]内单调。
整数二分
STL的lower_bound()和upper_bound()
通往奥格瑞玛的道路
最大值最小化
对点权fi进行二分(先排序),用dijkstra求最短路,检验总边权是否小于b。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=510005;
const int inf=10000000000;
inline LL read() {
LL x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,m,s,t;
int hd[N],to[N],nxt[N],tot=1;
LL w[N],dis[N],b;
inline void add(int x,int y,LL z) {
to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}
LL cost[N],f[N];
bool vis[N];
bool check(LL val) {
if(val<cost[1]||val<cost[n]) return 0;
for(int i=1;i<=n;i++) dis[i]=inf,vis[i]=0;
priority_queue< pair<LL,int> >q;
q.push(make_pair(0,1));
dis[1]=0;
while(!q.empty()) {
int x=q.top().second;q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(cost[y]>val||vis[y]) continue;
if(dis[y]>dis[x]+w[i]) {
dis[y]=dis[x]+w[i];
// cout<<y<<" "<<dis[y]<<endl;
if(!vis[y])
q.push(make_pair(-dis[y],y));
}
// if(y==n) return dis[n]<=b;
}
}
return dis[n]<=b;
}
int main() {
n=read();m=read();b=read();
for(int i=1;i<=n;i++) cost[i]=f[i]=read();
sort(f+1,f+1+n);
int x,y;LL z;
for(int i=1;i<=m;i++) {
x=read();y=read();z=read();
add(x,y,z),add(y,x,z);
}
int l=1,r=n,ans=0;
while(l<=r) {
int mid=(l+r)>>1;
if(check(f[mid])) ans=f[mid],r=mid-1;
else l=mid+1;
}
ans?printf("%lld
",ans):printf("AFK
");
return 0;
}
进击的奶牛
最小值最大化
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100005;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
int n,c,a[N];
bool check(int mid) {
int pre=a[1],cnt=1;
for(int i=2;i<=n;i++) {
if(a[i]-pre>=mid) {
if(++cnt==c) return 1;
pre=a[i];
}
}
return 0;
}
int main() {
n=read();c=read();
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+1+n);
int l=0,r=a[n]-a[1];
int ans=0;
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d
",ans);
return 0;
}
实数二分
// 1
while(l+eps<r) {
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
// 2 当eps不确定时用for更精确
for(int i=0;i<100;i++) {
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
切绳子
舍掉后几位:
方法1:floor(lb*100)/100
方法2:先所有的a[i] *100 最后答案除以100
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
int m,n;
double a[10005];
bool C(double x) {
int num=0;
for (int i=1;i<=n;i++)
num+=(int)(a[i]/x);
return num>=m;
}
int main() {
// freopen("1.in","r",stdin);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lf",&a[i]);
double lb=0,ub=0x7fffffff;
for (int i=1;i<=100;i++) {
double mid=(lb+ub)/2;
if(C(mid)) lb=mid; else ub=mid;
}
printf("%.2f
",floor(lb*100)/100);
return 0;
}
寻找段落
二分+单调队列好题
二分最大平均值。
一开始我是暴力O(N^2) 判断,只有30分;
正解:
我们将a全部减去mid,问题转化为判断是否存在一个长度在s~t范围内的区间它的和为正,如果有说明还有更大的平均值。
用前缀和和单调队列维护。
然后用单调队列求出sum[i]-min(sum[i-t]~sum[i-s]),然后判断是否大于0即可。
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
const int N=100005;
const int eps=1e-4;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,L,R,q[N];
double a[N],sum[N];
bool check(double mid) {
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i]-mid;
int h=1,t=0;
for(int i=L;i<=n;i++) {
while(h<=t&&sum[q[t]]>sum[i-L]) t--;
q[++t]=i-L;
while(h<=t&&q[h]<i-R) h++;
if(h<=t&&sum[i]-sum[q[h]]>=0) return 1;
}
return 0;
}
int main() {
n=read();L=read();R=read();
for(int i=1;i<=n;i++)
a[i]=read();
double l=0,r=10000,mid;
for(int i=0;i<100;i++) {
mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.3lf
",l);
return 0;
}
wait:
https://www.luogu.com.cn/problem/P1493