码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-08-25 14:32:23      阅读:162      评论:0      收藏:0      [点我收藏+]

标签:pac   数据   dream   printf   枚举   inf   简单的   区间   efi   

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

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

标签:pac   数据   dream   printf   枚举   inf   简单的   区间   efi   

原文地址:https://www.cnblogs.com/SZJZC/p/11407434.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!