题面描述
传送门
思路
状态转移方程
Fi=max(Fj,i−jsumi−sumj)
决策单调性
设有
j<k≤i−m
max(Fk,i−ksumi−sumk)≥max(Fj,i−jsumi−sumj)
如果
i−ksumi−sumk,i−jsumi−sumj
均对答案有贡献,那么
i−ksumi−sumk≥i−jsumi−sumj
则
(sumi−sumk)∗(i−j)≥(sumi−sumj)∗(i−k)
对于未来状态
t,若
t−ksumt−sumk,t−jsumt−sumj
仍对答案有贡献,
证明
t−ksumt−sumk≥t−jsumt−sumj
(sumt−sumk)∗(t−j)≥(sumt−sumj)∗(t−k)
由
sumk=sumi+val,t−j>t−k
(sumi−sumk)∗(t−j)≥(sumi−sumj)∗(t−k)
仅需证明
val∗(t−j)≥val∗(t−k)
由于
val>0
则成立。
因此
k永远优于
j
踢队头
calc(k,i)=i−ksumi−sumk≥calc(j,i)=i−jsumi−sumj
因此,根据上文
当
calc(k,i)≥calc(j,i)时,
k优于
j
当
calc(qhead+1,i)≥calc(qhead,i)时,
qhead+1优于
qhead
因此观出,
calc(qhead,i)随
head增大而减小,维护一个上凸壳。
踢队尾
calc(i−m,qtail−1)≤calc(qtail,qtail−1)
即可。
AC code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int size=1<<20;
inline char gc()
{
static char buf[size],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++;
}
#define gc gc()
inline void qr(int &x)
{
x=0;int f=1;char c=gc;if(c==EOF)return;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}x*=f;
}
int s[N];int q[N],l,r;int n,m;
inline double calc(int j,int i)
{
return (double)(s[i]-s[j])/(i-j);
}
int main()
{
while(qr(n),n)
{
qr(m);
s[0]=0;
for(int i=1;i<=n;i++)qr(s[i]),s[i]+=s[i-1];
l=1;r=0;q[1]=0;double ans=0;
for(int i=m;i<=n;i++)
{
while(l<r&&calc(q[r],q[r-1])>=calc(i-m,q[r-1]))--r;
q[++r]=i-m;
while(l<r&&calc(q[l+1],i)>=calc(q[l],i))++l;
ans=max(ans,calc(q[l],i));
}
printf("%.2lf\n",ans);
}
return 0;
}