HDU 2993 MAX Average Problem 斜率DP+IO优化
HDU 2993
给一个序列 ,定义子区间均值为 ( 为 的前缀和),并且要求子区间的长度不小于 ,现在求 。
由于 ,因此暴力是会超时的。
分析可以参考NOI2004年周源的论文
观察 我们不难发现这是一个斜率的形式,将 作为横坐标, 作为纵坐标画图, 的值即为 这两个点连线的斜率。目标也就是找哪两个点连线的斜率最大。
依次枚举两个点的复杂度是 ,会超时。定义斜率 ,对于 ,假如 ,那么对于 而言, ,因此 不会成为一个最优点。
所以要维护一个斜率单增的序列,当找以 为结尾的斜率最大值时,从单调队列的前面找。
不加上IO优化的话会超时,代码如下:
#include<iostream>
//#define WINE
#define MAXN 100010
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,k,a[MAXN],h,t,q[MAXN],tot;
double s[MAXN];
double res;
const int BUF=25000000;
char Buf[BUF],*buf=Buf;
void read(int &a){
for(a=0;*buf<48;buf++);
while(*buf>47)a=a*10+*buf++-48;
}
double up(int j,int k){
return s[j]-s[k];
}
int down(int j,int k){
return j-k;
}
int main(){
#ifdef WINE
freopen("data.in","r",stdin);
#endif
tot=fread(Buf,1,BUF,stdin);
while(true){
if(buf-Buf+1>=tot)break;
read(n),read(k);
for(int i=1;i<=n;i++){
//scanf("%d",&a[i]);
read(a[i]);
s[i]=s[i-1]+a[i];
}
h=t=0;q[t++]=0;res=0;
for(int i=k;i<=n;i++){
while(h+1<t&&up(i,q[h])*down(i,q[h+1])<up(i,q[h+1])*down(i,q[h]))
h++;
res=max(res,up(i,q[h])/down(i,q[h]));
int j=i-k+1;
while(h+1<t&&up(j,q[t-1])*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2])*down(j,q[t-1]))
t--;
q[t++]=j;
}
printf("%.2lf\n",res);
}
return 0;
}