洛谷 P1419 寻找段落(01分数规划,实数二分,单调队列)

传送门


解题思路

很巧妙的一个做法。
二分答案。
然后我们就可以通过把a[i]全部减去mid,把判断s~t个数的平均数是否大于mid,转化为是否存在一个区间,其平均数是否大于0。
乍一看仿佛没有什么优化,但是仔细想一想会发现,前者并不好实现,因为区间平均数与数字个数有关系,而后者只与和的正负有关系。
于是就成了一个单调队列的基本操作。
注意是实数域上二分,设置一个eps小于要求的精度即可。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1e5+5;
const double eps=0.0001;
int n,s,t;
double d[maxn],l=-10000,r=10000,a[maxn];
bool check(){
	deque<int> q;
	for(int i=1;i<=n;i++){
		if(!q.empty()&&i-q.front()>t) q.pop_front();
		if(i<s) continue;
		while(!q.empty()&&d[i-s]<d[q.back()]) q.pop_back();
		q.push_back(i-s);
		if(d[i]-d[q.front()]>=0) return 1;
	}
	return 0;
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>s>>t;
	for(int i=1;i<=n;i++) cin>>a[i];
	while(r-l>eps){
		double mid=(l+r)/2;
		for(int i=1;i<=n;i++) d[i]=a[i]-mid+d[i-1];
		if(check()) l=mid;
		else r=mid; 
	}
	cout<<fixed<<setprecision(3)<<l;
    return 0;
}
posted @ 2021-08-27 20:28  尹昱钦  阅读(81)  评论(0编辑  收藏  举报