题目大意:一个整数序列n,给定一个长度范围L~R,给定一个K,让求一个ans,ans=max(ans,(max(l,r)-min(l,r))/(r-l+k))。r-l+1在L到R之内。
仔细分析这是一道分数规划题目。首先这个答案肯定是单调的,我们可以二分答案,把求最优值的问题转化为判定性问题。那么我们只差一个check函数。我们二分一个mid,如果不考虑区间长度在L到R之内的话,最优的区间一定是左右两个端点正好是最值。那么我们可以分类讨论。如果a[r]>a[l],如果mid是最终答案,那么一定满足所有的(a[r]-a[l])/(r-l+k)<=mid,把(r-l+k)移过去,a[r]-rmid-(a[l]-lmid)<=kmid,这种情况下,显然我们可以维护一个单调队列,队头是关于i符合区间长度的a[l]-mid的最小值,然后每次更新答案的时候,我们需要先找到队头到i到第一个符合右端点是最大值的点,只需要用一个lower_bound就可以了。这样我们一定保证了右端点是最大值,然后每次答案是当前情况的最优值。对于a[l]>a[r]的情况同理。
当然,我们还有一种情况没有分析到。就是有可能那些符合左右端点都正好是最值的区间的长度都小于L,那么我们此时枚举长度为L的分别求一遍,肯定是最优的。所以我们可以用rmq求一下所有区间长度为L的最大值,当然仍然使用单调队列更优。
这道题写了很长时间,然后在BZOJ上Wa了6次,不知道哪儿出了问题,最后发现t组数据没有初始化ans(真是傻逼了)
#include<bits/stdc++.h>
#define eps 1e-6
using namespace std;
inline int read(){
char ch=getchar();int num=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){num=(num<<1)+(num<<3)+ch-'0';ch=getchar();}
return num*f;
}
const int N=1e6+10;
int t,n,k,l,r,head1,head2,tail1,tail2,head,tail,q1[N],q2[N],s[N],top,a[N];
double ans,f[N];
struct node{
int id;double val;
}q[N];
bool cmp(node x,node y){
return x.id<y.id;
}
bool check(double mid){
double mx=0;
head=tail=top=1;
for(int i=1;i<=n;++i) f[i]=(double)a[i]-mid*i;
q[1].id=1;q[1].val=f[1];s[1]=1;
for(int i=2;i<=n;++i){
while(top&&a[s[top]]<=a[i]) top--;
s[++top]=i;
while(head<=tail&&q[head].id<i-r+1) head++;
node t;t.id=s[top-1]+1;
int w=lower_bound(q+head,q+tail+1,t,cmp)-q;
if(w<=tail&&q[w].id<=i-l+1) mx=max(mx,f[i]-q[w].val);
while(head<=tail&&f[i]<q[tail].val) tail--;
q[++tail].val=f[i];q[tail].id=i;
}
head=tail=top=1;
for(int i=1;i<=n;++i) f[i]=(double)a[i]+mid*i;
q[1].id=1;q[1].val=f[1];s[1]=1;
for(int i=2;i<=n;++i){
while(top&&a[s[top]]>=a[i]) top--;
s[++top]=i;
while(head<=tail&&q[head].id<i-r+1) head++;
node t;t.id=s[top-1]+1;
int w=lower_bound(q+head,q+tail+1,t,cmp)-q;
if(w<=tail&&q[w].id<=i-l+1) mx=max(mx,q[w].val-f[i]);
while(head<=tail&&f[i]>q[tail].val) tail--;
q[++tail].val=f[i];q[tail].id=i;
}
if(mx-k*mid>=eps) return 1;
else return 0;
}
int main(){
t=read();
while(t--){
ans=0;
n=read(),k=read(),l=read(),r=read();
for(int i=1;i<=n;++i) a[i]=read();
head1=head2=1;tail1=tail2=0;
for(int i=1;i<l;++i){
while(head1<=tail1&&a[q1[tail1]]<a[i]) tail1--;
q1[++tail1]=i;
while(head2<=tail2&&a[q2[tail2]]>a[i]) tail2--;
q2[++tail2]=i;
}
for(int i=l;i<=n;++i){
if(head1<=tail1&&q1[head1]<i-l+1) head1++;
if(head2<=tail2&&q2[head2]<i-l+1) head2++;
while(head1<=tail1&&a[q1[tail1]]<a[i]) tail1--;
q1[++tail1]=i;
while(head2<=tail2&&a[q2[tail2]]>a[i]) tail2--;
q2[++tail2]=i;
ans=max(ans,(double)1.0*(a[q1[head1]]-a[q2[head2]])/(l-1+k));
}
double L=0,R=1000;
while(L+eps<=R){
double mid=((L+R)*0.5);
if(check(mid)) ans=max(ans,mid),L=mid;
else R=mid;
}
printf("%.4lf\n",ans);
}
return 0;
}