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;
}

 

posted @ 2018-10-30 21:28  Gloid  阅读(154)  评论(0编辑  收藏  举报