Luogu 1419(二分答案+單調隊列)

傳送門

題意:求最大段落平均值(子段和除以長度),段落長度在S到T之間

題解:段落平均值一定在某個區間[min{a[1],a[2],...,a[n]},v]內取值(雖然取值是離散的但是有界),v即爲所求最大平均值。考慮二分答案,當前二分的答案爲mid,如果將所有數都減去mid後仍存在一個長度在S到T之間的子段和非負,那該段落的平均值一定不小於mid。滿足所有數都減去mid後仍存在一個長度符合要求的子段和非負的前提下,不斷將mid最大化,最終的mid就是所求最大段落平均值。

至於如何判斷存在一個長度在S到T之間的子段和非負,先求一次前綴和sum,然後對於每個i維護[i-T,i-S]區間內sum的最小值s,如果維sum[i]-s非負那一定存在一個長度在S到T之間的,以i結尾的子段和非負,否則一定不存在滿足條件的以i結尾的子段。既然[i-T,i-S]區間長度固定,使用“滑動窗口”模型,用單調隊列維護[i-T,i-S]區間內sum的最小值,複雜度爲線性。

總複雜度爲O(n(logS)),S爲mid的取值範圍長度,約爲20。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+4; 
const double eps=1e-4;
int n;
int S,T;
double a[N],sum[N];
int q[N];
inline void init(double x) {
	for (register int i=1;i<=n;++i)
		sum[i]=sum[i-1]+a[i]-x;
}
inline bool ok(double delta) {
	init(delta);
	int h=0,t=1;
	for (register int i=S;i<=n;++i) {
		while (h>=t&&sum[q[h]]>sum[i-S]) --h;
		q[++h]=i-S;
		while (h>=t&&i-q[t]>T) ++t;
		if (h>=t&&sum[i]-sum[q[t]]>=0) return true;
	}
	return false;
}
int main() {
	scanf("%d%d%d",&n,&S,&T);
	for (register int i=1;i<=n;++i)
		scanf("%lf",&a[i]);
	double l=-10000,r=10000*n;
	while (r-l>eps) {
		double mid=(l+r)/2.0;
		if (ok(mid)) l=mid;
		else r=mid;
	}
	printf("%.3lf\n",l);
	return 0;
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章