HDU 2993 MAX Average Problem 斜率DP+IO优化

HDU 2993
给一个序列 {a[i],i[1,n]}\{a[i],i\in[1,n]\},定义子区间均值为 avg(i,j)=s[j]s[i1]ji+1avg(i,j)=\dfrac{s[j]-s[i-1]}{j-i+1}s[i]s[i]a[i]a[i] 的前缀和),并且要求子区间的长度不小于 kk,现在求 max{avg(i,j),1ijk+1n}\max\{avg(i,j),1\le i\le j-k+1\le n\}
由于 n105n\le 10^5 ,因此暴力是会超时的。
分析可以参考NOI2004年周源的论文

观察 avg(i,j)avg(i,j) 我们不难发现这是一个斜率的形式,将 ii 作为横坐标,s[i]s[i] 作为纵坐标画图,avg(i,j)avg(i,j) 的值即为 (i1,s[i1]),(j,s[j])(i-1,s[i-1]),(j,s[j]) 这两个点连线的斜率。目标也就是找哪两个点连线的斜率最大。

依次枚举两个点的复杂度是 O(n2)O(n^2) ,会超时。定义斜率 sij=s[i]s[j]ijs_{ij}=\dfrac{s[i]-s[j]}{i-j},对于 k<j<ik<j<i,假如 sij<sjks_{ij}<s_{jk} ,那么对于 ii' 而言,si,i>si,js_{i',i}>s_{i',j} ,因此 jj 不会成为一个最优点。

所以要维护一个斜率单增的序列,当找以 ii 为结尾的斜率最大值时,从单调队列的前面找。
不加上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;
}

在这里插入图片描述
在这里插入图片描述

posted @ 2020-03-12 14:03  winechord  阅读(90)  评论(0编辑  收藏  举报