GDOI#345. 送礼物「JSOI 2015」01分数规划+RMQ

Posted zcr-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GDOI#345. 送礼物「JSOI 2015」01分数规划+RMQ相关的知识,希望对你有一定的参考价值。

题目描述

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

(M(i,j)-m(i,j))/(j-i+k)(M(i,j)m(i,j))/(ji+k),其中M(i,j)M(i,j)表示max\A_i,A_i+1....A_j\maxAi?,Ai+1?....Aj?m(i,j)m(i,j)表示min\A_i,A_i+1....A_j\minAi?,Ai+1?....Aj?,K为给定的正整数。

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

法一:用单调暴力求解,然后你就可以得到宝贵的20分(本人亲自实验)。

法二(正解):

若区间长度等于规定L,就直接用单调队列维护长度为L的区间的最大值和最小值,分别计算每个区间,用一个ans记录最大值。

若区间大于L,对于一个区间[l, r]很明显可以发现最优的取法是在A[l]为最小值, A[r]为最大值或A[l]为最小值,A[r]为最大值。

然后开始二分答案。

judge函数:

1, A[l] > A[r], 要A[l] - A[r] > (r - l + 1) * mid; 所以若maxA[i] + i * mid - (A[j] - j * mid) - k * mid >= 0则mid可以更大

2,若A[l] < A[r] 同理,反过来就行。

所有A[i] + i * mid, A[j] - j * mid用单调队列来维护。

#include <bits/stdc++.h>
using namespace std;
const long long MAX = 500005;
const double INF = 1e9;
long long t, n, k, l, r;
long long a[MAX], q1[MAX], q2[MAX], q[MAX];
double val[MAX];
double ans;
//读入优化 
double read() 
    double ret = 0, f = 1;
    char ch = getchar();
    while (0 > ch || ch > 9) 
        if (ch == -) f = -1;
        ch = getchar();
    
    while (0 <= ch && ch <= 9) 
        ret = ret * 10 + ch - 0;
        ch = getchar();
    
    return ret * f;

//判断 
bool judge(double m) 
    double ret = -INF;
    for (long long i = 1; i <= n; i++) 
        val[i] = a[i] - m * i;
    
    long long head = 1, tail = 0;
    for (long long i = l + 1; i <= n; i++) 
        while (head <= tail && i - q[head] >= r) head++;
        while (head <= tail && val[q[tail]] >= val[i - l]) tail--;
        q[++tail] = i - l;
        ret = max(ret, val[i] - val[q[head]]);
    
    for (long long i = 1; i <= n; i++) 
        val[i] = a[i] + m * i;
    
    head = 1, tail = 0;
    for (long long i = n - l; i >= 1; i--) 
        while (head <= tail && q[head] - i >= r) head++;
        while (head <= tail && val[q[tail]] >= val[i + l]) tail--;
        q[++tail] = i + l;
        ret = max(ret, val[i] - val[q[head]]);
    
    //k是题目给的常数 
    return ret >= k * m;

int main() 
    t = read();
    while (t--) 
        ans = -INF;
        n = read(), k = read(), l = read(), r = read();
        for (long long i = 1; i <= n; i++) a[i] = read();
        long long h1 = 1, h2 = 1, t1 = 0, t2 = 0;
        for (long long i = 1; i < l; i++) 
            while (h1 <= t1 && a[q1[t1]] >= a[i]) t1--;
            while (h2 <= t2 && a[q2[t2]] <= a[i]) t2--;
            q1[++t1] = q2[++t2] = i; 
        
        for (long long i = 1; i <= n; i++) 
            while (h1 <= t1 && i - q1[h1] >= l) h1++;
            while (h2 <= t2 && i - q2[h2] >= l) h2++;
            while (h1 <= t1 && a[q1[t1]] >= a[i]) t1--;
            while (h2 <= t2 && a[q2[t2]] <= a[i]) t2--;
            q1[++t1] = q2[++t2] = i;
            ans = max(ans, 1.0 * (a[q2[h2]] - a[q1[h1]]) / (l + k - 1));
        
        //注意精度 
        double l = 0, r = 1000;
        while (r - l >= 0.000001) 
            double mid = (l + r) / 2;
            if (judge(mid)) ans = max(ans, mid), l = mid + 0.000001;
            else r = mid - 0.000001;
        
        printf("%.4lf\n", ans);
    
    return 0;

 

以上是关于GDOI#345. 送礼物「JSOI 2015」01分数规划+RMQ的主要内容,如果未能解决你的问题,请参考以下文章

luogu P6087 [JSOI2015]送礼物 二分 单调队列 决策单调性

互相送礼物

TYVJ1340 送礼物

CH2401 送礼物(双向dfs)

仿QQ空间送礼物功能

upc7834 送礼物