标签:二分,单调队列
题目描述
给定一个长度为n的序列a_i,定义a[i]为第i个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在[S,T]之间的连续序列。最有价值段落是指平均值最大的段落,
段落的平均值=段落总价值/段落长度。
输入输出格式
输入格式:
第一行一个整数n,表示序列长度。
第二行两个整数S和T,表示段落长度的范围,在[S,T]之间。
第三行到第n+2行,每行一个整数表示每个元素的价值指数。
输出格式:
一个实数,保留3位小数,表示最优段落的平均值。
输入输出样例
输入样例#1:
3
2 2
3
-1
2
输出样例#1:
1.000
说明
【数据范围】
对于30%的数据有n<=1000。
对于100%的数据有n<=100000,1<=S<=T<=n,-10000<=价值指数<=10000。
题意:给定长度为N的序列
求:max{(a[i]+a[i+1]+……+a[i+j])/j} s<=j<=t
看题的第一眼想到了单调队列,但是貌似会被卡掉TLE
正解是二分答案,判断每次的mid是否可行
Sum[i]表示前i个数减去过去mid的总和
对于第i个数,如果sum[i]>=min{sum[i-y]->sum[i-x]}那么当前方案可行
对于min{sum[i-y]~sum[i-x]},我们用一个单调队列维护就行了
Code
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
#define mem(x,num) memset(x,num,sizeof x)
using namespace std;
const int maxn=1e5+6,inf=1e9;
int a[maxn],tt[maxn];
LL c[maxn],sum[maxn];
int n,i,l,r,x,y,mid,ans=0;
inline bool check(int k){
mem(c,0);mem(tt,0);
rep(i,1,n)sum[i]=sum[i-1]+a[i]-k;
int head=1,tail=0;
rep(i,1,n){
int j=i-x;
if(j<0)continue;
while((sum[j]<c[tail])&&(head<=tail))tail--;
c[++tail]=sum[j];
tt[tail]=j;
if(tt[head]<(i-y))head++;
if(sum[i]>=c[head])return true;
}
return false;
}
int main()
{
scanf("%d%d%d",&n,&x,&y);
rep(i,1,n)scanf("%d",&a[i]),a[i]*=1e4;
l=-inf;r=inf;
while(l<=r){
mid=(l+r)>>1;
if(check(mid)){l=mid+1;ans=mid;}
else r=mid-1;
}
printf("%.3lf",ans/1e4);
return 0;
}