题解
通过贪心的思路,我们可以知道最大值与最小值一定是在区间的两个端点的。
先二分一个答案,然后我们可以得到,
ai∗mid−ai>aj∗mid−aj+mid∗k
用
ai∗mid−ai
维护一个单调队列,
然后枚举一个右端点,即最大值,
判断是否满足
ai∗mid−ai>aj∗mid−aj+mid∗k
还有一种特殊情况,
就是最小值在限制的l以内,那么只能强制将这个区间扩大到l。
code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
double l,r,mid,ans,a[50003];
int n,k,T,s,t,z[50003],L,R;
double d[50003];
bool work()
{
s=1;t=0;
for(int j=L;j<=n;++j)
{
while(z[s]+R<=j)s++;
int i=j-L+1;
while(s<=t && d[t]<=mid*i-a[i])t--;
t++;d[t]=mid*i-a[i];z[t]=i;
if(d[s]>mid*j-a[j]+mid*k)return 1;
}
return 0;
}
void work1()
{
s=t=1;
z[1]=1;
d[1]=a[1];
for(int j=2;j<=n;j++)
{
while(z[s]+L<=j)s++;
ans=max(ans,(a[j]-d[s])/(L-1+k));
while(s<=t && d[t]>=a[j])t--;
t++;d[t]=a[j];z[t]=j;
}
}
void get()
{
l=0;r=1000;
while(l+0.0000001<=r)
{
mid=(l+r)/2;
if(work())l=mid+0.0000001;else r=mid;
}
ans=max(ans,mid);
work1();
}
int main()
{
freopen("gift.in","r",stdin);
freopen("gift.out","w",stdout);
scanf("%d",&T);
while(T--)
{
ans=0;
scanf("%d%d%d%d",&n,&k,&L,&R);
for(int i=1;i<=n;i++)
scanf("%lf",&a[i]);
get();
for(int i=1;i<=n/2;i++)swap(a[i],a[n-i+1]);
get();
printf("%.4f\n",ans);
}
}