二分(答案)

二分(答案)

什么情况下用二分?两个条件:上下界[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\n",ans):printf("AFK\n");
	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\n",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\n",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\n",l);
	return 0;
}


wait:

https://www.luogu.com.cn/problem/P1493

聪明的质监员

https://www.luogu.com.cn/problem/P1314

posted @ 2020-09-02 22:05  ke_xin  阅读(47)  评论(0编辑  收藏  举报