送礼物(二分答案+单调队列)

Posted szjzc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了送礼物(二分答案+单调队列)相关的知识,希望对你有一定的参考价值。

QUESTION:
JYY和CX的结婚纪念日即将到来,JYY来到萌萌开的礼品店选购纪念礼物。萌萌的礼品店很神奇,所有出售的礼物都按照特定的顺序都排成一列,而且相邻的礼物之间有一种神秘的美感。于是,JYY决定从中挑选连续的一些礼物,但究竟选 哪些呢?假设礼品店一共有\\(N\\)件礼物排成一列,每件礼物都有它的美观度。排在第\\(i\\)(\\(1\\leq i \\leq N\\))个位置的礼物美观度为正整数\\(A_i\\)。JYY决定选出其中连续的一段,即编号为礼物\\(i\\),\\(i+1\\),....,\\(j-1\\),\\(j\\)的礼物。选出这些礼物的美观程度定义为:

(\\(M(i,j)\\)-\\(m(i,j)\\))\\(\\div\\)\\((j-i+k)\\)

其中\\(M\\)\\((\\)\\(i\\)\\(,\\)\\(j\\)\\()\\)表示\\(max\\)\\(A_i\\),\\(A_i+1\\)\\(,\\)\\(....\\)\\(A_j\\)
\\(m\\)\\((\\)\\(i\\)\\(,\\)\\(j\\)\\()\\)表示\\(min\\)\\(A_i\\),\\(A_i+1\\)\\(,\\)\\(....\\)\\(A_j\\)\\(,\\)\\(k\\)为给定的正整数。

由于不能显得太小气,所以\\(JYY\\)\\(所\\)选礼物的件数最少为\\(L\\)件;同时,选得太多也不好拿,因此礼物最多选\\(R\\)件。\\(JYY\\)应该如何选择,才能得到最大的美观程度?由于礼物实在太多挑花眼,\\(JYY\\)打算把这个问题交给会编程的你。

输入格式

本题每个测试点有多组数据。
输入第一行包含一个正整数\\(T\\)(\\(T \\leq 10\\)),表示有\\(T\\)组数据。
每组数据包含两行.
第一行四个非负整数\\(N\\),\\(K\\),\\(L\\),\\(R\\)(\\(2\\leq L\\leq R\\leq N\\))。
第二行包含N个正整数,依次表示\\(A_1,A_2....A_n\\)(\\(A_i\\leq 10^8\\))\\(N,K\\leq 50,000\\)

输出格式

输出\\(T\\)行,每行一个非负实数,依次对应每组数据的答案,数据保证答案不会超过\\(10^3\\)。输出四舍五入保留\\(4\\)位小数。

往下\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















继续

















W

















q

















t

















AK

















IOI!

















快到了


































Codefoces


































毁我人生!


































40M

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















30M

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















\\(\\bigvee\\)

















20M\\(\\bigvee\\)







\\(\\bigvee\\)







\\(\\bigvee\\)







\\(\\bigvee\\)







\\(\\bigvee\\)







\\(\\bigvee\\)







\\(\\bigvee\\)







\\(\\bigvee\\)







\\(\\bigvee\\)








10M









\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)








\\(\\bigvee\\)

















XIBER!!!
技术图片
解答:
作为比赛压轴,只有1人20分,其余10分,看了一下,还都是打表,也不知道老师为什么没有禁掉QWQ
PS:我们的AKDREAM同学这题由于每组数据没有重新初始化,送走了20分QAQ
但这道题不打表有一个明显的做法,枚举长度,从\\(L\\)开始枚,记录运算了多少次,然后超过4*\\(10^7\\)就退出(否则TLE),可以拿20分^_^
可以简单的发现,如果不考虑礼物个数的话,选出的区间应该两端为最大值与最小值(否则可以将不是最值的一端向里"缩",到最值为止,此时\\(M(i,j)\\),\\(m(i,j)\\)不变,\\((j-i+k)\\)变小,总体变大.
考虑到题目限制,需要先枚举长度为\\(L\\)的情况,用ST表或单调队列均可,这里选用单调队列.
之后可以采用二分答案mid,每一次a[L],a[R]一个最大,一个最小.,则max((A[i]?i?mid)?(A[j]?j?mid)?k?mid)与max((A[i]+i?mid)?(A[j]+j?mid)?k?mid)有一个大于等于0即可,原因很简单,不作说明.
其中A[i]?i?mid,A[i]+i?mid可以用单调队列维护.




上代码:

//JZC de 工业化代码

#include <bits/stdc++.h>
#define ll long long
#define MAX 50001
using namespace std;
ll t,n,k,l,r,head1,head2,tail1,tail2,head,tail;
ll q[MAX],q1[MAX],q2[MAX],a[MAX];
double cnt[50005],ans,Ans;
void I_P1(int i)
    while(head<=tail&&i>=q[head]+(r-l))head++;
    while(head<=tail&&cnt[q[tail]]>=cnt[i])tail--;
    q[++tail]=i;

void I_P2(int i)
    while(head<=tail&&q[head]-i>=r-l)head++;
    while(head<=tail&&cnt[q[tail]]>=cnt[i])tail--;
    q[++tail]=i;

bool check(double mid) 
    for(int i=1;i<=n;i++)cnt[i]=a[i]-mid*i;
    head=1;tail=0;Ans=-100000000.0;
    for(int i=1;i<=n-l;i++)I_P1(i),Ans=max(Ans,cnt[i+l]-cnt[q[head]]);
    for(int i=1;i<=n;i++)cnt[i]=a[i]+mid*i;
    head=1;tail=0;
    for(int i=n;i>l;i--)I_P2(i),Ans=max(Ans,cnt[i-l]-cnt[q[head]]);
    return Ans>=k*mid;

void Push(int i)//更新,将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;

void Update(int i)//更新,将长度超标的弹出队列
    while(head1<=tail1&&i>=q1[head1]+l)head1++;
    while(head2<=tail2&&i>=q2[head2]+l)head2++;

int main() 
    cin>>t;
    while (t--) 
        cin>>n>>k>>l>>r;
        for(int i=1;i<=n;i++)cin>>a[i];
        head1=head2=1;
    tail1=tail2=0;
        //---计算长度为L的最大值---BEGIN 
        for(int i=1;i<l;i++)Push(i);
        ans=-1e9;
        for (int i=l;i<=n;i++) 
            Update(i),Push(i);
            ans=max(ans,((double)(a[q2[head2]]-a[q1[head1]]))/(double)(l+k-1));
        
        //---计算长度为L的最大值---END
        //---二分答案---BEGIN
        double l=0.0,r=10000.0;
        while(r>l+(1e-9))
            double mid=(l+r)/2;
            if(check(mid))ans=max(ans,mid),l=mid;//更新
            else r=mid-(1e-7);
        
        //---二分答案---END
        printf("%.4lf\\n", ans);
    

$ Tips$:
1.队列较多,千万不要搞混,血的教训QAQ
2.二分时每次需要-(1e-7),不然会玄学RE

以上是关于送礼物(二分答案+单调队列)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列

luoguP1419 寻找段落(二分答案+单调队列)

P3957 跳房子[二分答案][dp][单调队列]

bzoj3316 JC loves Mkk 二分答案 单调队列

4/6 深搜广搜专题+二分答案+单调队列

[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)