您的位置:首页 > Web前端 > JavaScript

BZOJ4476 JSOI2015送礼物(分数规划+单调队列)

2018-10-30 21:28 501 查看

  看到这个式子当然先二分答案。得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
,p
,q
;
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;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: