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

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

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值