二分答案+队列的运用

题目描述

序列(sequence)
【题目背景】
在人类智慧的山巅, 有着一台字长为 1048576 位的超级计算机, 著名理
论计算机科学家 Pb 博士正用它进行各种研究。 不幸的是, 这天台风切断了电
力系统, 超级计算机无法工作, 而 Pb 博士明天就要交实验结果了, 只好求助
于学过 OI 的你… …
【题目描述】
Pb 博士将他的计算任务抽象就是询问一个序列的最优值。
具体来说有一个长度为 n 的序列 a
他想询问这个序列的子序列的平均数
Pb 博士解释说子序列的平均数为子序列的和除以子序列的长度
Pb 博士发现他的计算机似乎出了点问题,只能求长度介 S,T(S<=T)之间的
子序列的平均值的最大值
Pb 博士想让你设计一个程序帮他求出这个最大值
【输入格式】
从文件 sequence.in

中读入数据。
输入的第一行 1 个正整数 n;n 的含义见题目描述。
输入的第二行 2 个正整数 S,T;S,T 的含义见题目描述,数据保证 S<=T
接下来一行 n 个数, 表示序列 a。
同一行输入的相邻两个元素之间, 用恰好一个空格隔开。
【输出格式】
输出到文件 sequence.out 中。
输出包括一行表示平均数的最大值, 精确到小数点后 4 位
【样例输入】
5 2
3
6 -4 0 1 -3
【样例输出】
1.0000
【样例解释】
长度为 2 的子序列的平均数的最大值是 1.0000。 即子序列[6,-4]
长度为 3 的子序列的平均数的最大值是 0.6667。 即子序列[6,-4,0]
故答案为 1.0000
【子任务】
数据保证 a[i]∈[-10000,10000]
数据保证序列 a 随机生成

题目分析

这道题我们可以用暴力来做,直接枚举长度去求最大值,在出题人给的数据中能够得到55分的高分。当然如果不满足于此,想多得点分,这样我们可以想想二分答案,每次二分平均值,然后check一下,check的过程主要是为了判断以这个数做答案的情况下,是否合法,如果合法,则可以继续二分更大的值。具体看程序

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define fp(i,a,b) for(register int i=a;i<=b;++i)
using namespace std;
int q[2000005],a[2000005];
long long sum[2000005];
int n,s,t,ans;
//此题跟之前有一道用单调队列维护的题很像,可以做做那一道 
inline int read(){
    int x=0,w=1;char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
    return x*w;
}
inline int check(int x){//check的时候用到了单调队列优化
   fp(i,1,n) sum[i]=sum[i-1]+a[i]-x;//记录一下前缀和,减去平均值后的前缀和
    int h=1,ll=0;
    fp(i,1,n){
        if(i>=s){
            while(h<=ll&&sum[i-s]<sum[q[ll]]) --ll;
            q[++ll]=i-s;
        }
        while(h<=ll&&q[h]<i-t)++h;
        if(h<=ll&&sum[i]-sum[q[h]]>=0) return 1;
        //关键是这里,如果当前枚举的这个i的前缀和减去队首所存的位置的前缀和,如果还是大于0,则说明是合法的。
    }
    return 0;
}
int main(){
    freopen("sequence.in ","r",stdin);
    freopen("sequence.out","w",stdout);
    n=read();s=read();t=read();
    fp(i,1,n) a[i]=read(),a[i]*=100000;
    //因为有精度要求,将数字扩大,方便操作些
    int l=-1e9,r=1e9,mid;
    while(l<=r){//二分答案
        mid=(l+r)>>1;
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }   
    printf("%.4lf",(double)ans/100000);
}
/*
5 2
3
6 -4 0 1 -3
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值