BZOJ4476 JSOI2015送礼物(分数规划+单调队列)
看到这个式子当然先二分答案。得max-min-(j-i+k)ans>=0。
显然max-min相同的情况下所选区间长度越短越好,所以max和min都应该取在边界。那么实际上我们根本不用管端点是否真的是max或min,因为即使不是将他们计入也不会对最终答案造成影响。不妨设右端点是max,则要最大化aj-ai-(j-i)ans=(aj-jans)-(ai-ians),单调队列维护即可。左端点是max同理。
为了防止不存在长度在l~r的这样的区间,先对长度l的区间单调队列暴力跑一次。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 50010 const double eps=1E-8; int T,n,k,l,r,a[N],p[N],q[N]; bool check(double ans) { int head=1,tail=0,head1=1,tail1=0; for (int i=1;i<=n;i++) { while (head<=tail&&p[head]<=i-l) head++; while (head<=tail&&a[i]>=a[p[tail]]) tail--; p[++tail]=i; while (head1<=tail1&&q[head1]<=i-l) head1++; while (head1<=tail1&&a[i]<=a[q[tail1]]) tail1--; q[++tail1]=i; if (i>=l&&a[p[head]]-a[q[head1]]>=(l+k-1)*ans) return 1; } head=1,tail=0; for (int i=1;i<=n;i++) { while (head<=tail&&q[head]<=i-r) head++; if (i>=l) { while (head<=tail&&a[q[tail]]-q[tail]*ans>=a[i-l+1]-(i-l+1)*ans) tail--; q[++tail]=i-l+1; if ((a[i]-i*ans)-(a[q[head]]-q[head]*ans)>=k*ans) return 1; } } head=1,tail=0; for (int i=1;i<=n;i++) { while (head<=tail&&q[head]<=i-r) head++; if (i>=l) { while (head<=tail&&a[q[tail]]+q[tail]*ans<=a[i-l+1]+(i-l+1)*ans) tail--; q[++tail]=i-l+1; if ((a[q[head]]+q[head]*ans)-(a[i]+i*ans)>=k*ans) return 1; } } return 0; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4476.in","r",stdin); freopen("bzoj4476.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif T=read(); while (T--) { n=read(),k=read(),l=read(),r=read(); for (int i=1;i<=n;i++) a[i]=read(); double L=0,R=1000,ans; while (L+eps<=R) { double mid=(L+R)/2; if (check(mid)) ans=mid,L=mid+eps; else R=mid-eps; } printf("%.4f\n",ans); } return 0; }