bzoj4476: [Jsoi2015]送礼物 分数规划

bzoj4476: [Jsoi2015]送礼物

Description

JYY和CX的结婚纪念日即将到来,JYY来到萌萌开的礼品店选购纪念礼物。
萌萌的礼品店很神奇,所有出售的礼物都按照特定的顺序都排成一列,而且相邻
的礼物之间有一种神秘的美感。于是,JYY决定从中挑选连续的一些礼物,但究
竟选哪些呢?
【问题描述】
假设礼品店一共有N件礼物排成一列,每件礼物都有它的美观度。排在第i
1< =i< =N个位置的礼物美观度为正整数Ai,。JYY决定选出其中连续的一段,
即编号为礼物i,i+1,…,j-1,j的礼物。选出这些礼物的美观程度定义为
(M(i,j)-m(i,j))/(j-i+k)
其中M(i,j)表示max{Ai,Ai+1….Aj},m(i,j)表示min{Ai,Ai+1….Aj},K为给定的正整数。
由于不能显得太小气,所以JYY所选礼物的件数最少为L件;同时,选得太
多也不好拿,因此礼物最多选R件。JYY应该如何选择,才能得到最大的美观程
度?由于礼物实在太多挑花眼,JYY打算把这个问题交给会编程的你。

Input

本题每个测试点有多组数据。输入第一行包含一个正整数T(T< =10),表示
有T组数据。
每组数据包含两行,第一行四个非负整数N,K,L,R(2< =L< =R< =N。第二行
包含N个正整数,依次表示A1,A2….An,(Ai< =10^8),N,K< = 50,000

Output

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

Sample Input

1
5 1 2 4
1 2 3 4 5

Sample Output

0.7500

分析

答案似乎好像确凿是满足单调性的。
然后就可以愉快地分数规划了。
maxi=lraimini=lrairl+kv max i = l r a i − min i = l r a i r − l + k ≥ v

maxi=lraimini=lraiv(rl+k) max i = l r a i − min i = l r a i ≥ v ⋅ ( r − l + k )

maxi=lraimini=lraiv(rl+k) max i = l r a i − min i = l r a i ≥ v ⋅ ( r − l + k )

maxi=lr{ai+vl}mini=lr{ai+vr}vk max i = l r { a i + v ⋅ l } − min i = l r { a i + v ⋅ r } ≥ v ⋅ k
这个时候还要发现一个结论。
除非区间长度为L(就是被它卡住了),否则区间头尾一定是最值。
那么首先处理长度等于L的情况,rmq就好啦。
不妨设区间头是最大值。上面的式子可以改写成
max1ijn,L<ji+1R{(ai+vi)(aj+vjj)}vk max 1 ≤ i ≤ j ≤ n , L < j − i + 1 ≤ R { ( a i + v ⋅ i ) − ( a j + v j ⋅ j ) } ≥ v ⋅ k
我们新建数列 bi=ai+vi b i = a i + v ⋅ i ,问题就转化为
max1ijn,L<ji+1R{bibj}vk max 1 ≤ i ≤ j ≤ n , L < j − i + 1 ≤ R { b i − b j } ≥ v ⋅ k
单调队列扫一遍即可。
对于区间头是最小值的,区间反转再做一遍即可。

代码

/**************************************************************
    Problem: 4476
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:3280 ms
    Memory:10472 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<vector>
using namespace std;
const int N = 50010;
const double eps = 1e-6;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
int Log[N], Bin[21], mn[21][N], mx[21][N], a[N], q[N], n, k, L, R;
double b[N];
int bet(int x, int y, int op) {return op ? max(x, y) : min(x, y);}
void pre(int F[21][N], int op) {
    for(int i = 1;i <= Log[n]; ++i)
        for(int j = 1;j + Bin[i] - 1 <= n; ++j)
            F[i][j] = bet(F[i - 1][j], F[i - 1][j + Bin[i - 1]], op);
}
int Que(int F[21][N], int L, int R, int op) {
    int t = Log[R - L + 1];
    return bet(F[t][L], F[t][R - Bin[t] + 1], op);
}
bool work(double v) {
    for(int i = 1; i <= n; ++i) b[i] = (double)a[i] - (double)i * v;
    double o = v * k; int l = 1, r = 0;
    for(int i = 1;i <= n - L; ++i) {
        if(l <= r && i + L - q[l] + 1 > R) ++l;
        while(l <= r && b[q[r]] >= b[i]) --r;
        q[++r] = i;
        if(b[i + L] - b[q[l]] >= o) return true;
    }
    if(n - q[l] + 1 > L && b[n] - b[q[l]] >= o) return true;
    return false;
}
bool check(double v) {
    if(work(v)) return true;
    for(int i = 1;i <= (n >> 1); ++i) swap(a[i], a[n - i + 1]);
    if(work(v)) return true;
    return false;
}
int main() {
    Log[0] = -1; for(int i = 1;i <= 50000; ++i) Log[i] = Log[i >> 1] + 1;
    Bin[0] = 1; for(int i = 1;i <= 20; ++i) Bin[i] = Bin[i - 1] << 1;
    for(int T = read(); T--; ) {
        n = read(); k = read(); L = read(); R = read(); double ans = 0;
        for(int i = 1;i <= n; ++i) a[i] = mx[0][i] = mn[0][i] = read();
        pre(mn, 0); pre(mx, 1);
        for(int i = 1;i + L - 1 <= n; ++i)
            ans = max(ans, (double)(Que(mx, i, i + L - 1, 1) - Que(mn, i, i + L - 1, 0)) / (L - 1 + k));
        double l = 0, r = 1000;
        while(l + eps < r) {
            double mid = (l + r) * 0.5;
            if(check(mid)) l = mid + eps;
            else r = mid - eps;
        }
        printf("%.4lf\n", max(l - eps, ans));
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值