題意:求最大段落平均值(子段和除以長度),段落長度在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;
}