题目描述
序列(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
*/