• [题解] [JSOI2015] 送礼物


    题面

    题解

    首先我们得先知道一个小结论, 就是说如果最优方案中最大值和最小值必定是在区间两端

    要么就是这两个数在长度为 (L) 的区间中

    那么我们用 ST表处理出长度为 (L) 的区间中最优的答案

    接下来处理两个数在区间首尾的方案

    假设区间右端是最大值, 左端是最小值

    左端最大右端最小把数组 (reverse) 一下就行

    考虑用上分数规划那套分析方法

    [displaystyleegin{aligned}frac{M(l, r) - m(l, r)}{r - l + k}&geq ans\M(l, r) - m(l, r) &geq (r - l + k) * ans\(M(l, r) - r * ans) - (m(l, r) - l * ans) &geq k * ans\end{aligned} ]

    如果确定了 (ans) , 那么左边的 (M(l, r) - r * ans) 就是关于位置 (r) 的一个定值

    发现这个东西可以单调队列

    做完了, 二分一下这个 (ans) 就行

    Code

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    const int N = 50005;
    const double eps = 1e-6; 
    using namespace std;
    
    int T, n, m, L, R, a[N], f[2][15][N], q[N];
    double res, ans, b[N]; 
    
    template < typename T >
    inline T read()
    {
        T x = 0, w = 1; char c = getchar();
        while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * w; 
    }
    
    void getst()
    {
        for(int i = 1; (1 << i) <= n; i++)
    	for(int j = 1; j + (1 << i) - 1 <= n; j++)
    	{
    	    f[0][i][j] = min(f[0][i - 1][j], f[0][i - 1][j + (1 << (i - 1))]);
    	    f[1][i][j] = max(f[1][i - 1][j], f[1][i - 1][j + (1 << (i - 1))]); 
    	}
    }
    
    int query(int opt, int l, int r)
    {
        int tmp = log(1.0 * r - l + 1) / log(2.0); 
        if(opt) return max(f[1][tmp][l], f[1][tmp][r - (1 << tmp) + 1]);
        else return min(f[0][tmp][l], f[0][tmp][r - (1 << tmp) + 1]); 
    			 
    }
    
    bool check(double mid)
    {
        int l = 1, r = 0, flag = 0; q[++r] = 1;
        for(int i = 1; i <= n; i++) b[i] = a[i] - mid * i; 
        for(int i = L; i <= n; i++)
        {
    	if(b[i] - b[q[l]] >= mid * m) flag |= 1; 
    	while(l <= r && q[l] < i + 2 - R) l++; 
    	while(l <= r && b[q[r]] >= b[i - L + 2]) r--; 
    	q[++r] = i - L + 2; 
        }
        return flag; 
    }
    
    void clear() { res = ans = 0; }
    
    int main()
    {
        T = read <int> ();
        while(T--)
        {
    	clear(), n = read <int> (), m = read <int> (), L = read <int> (), R = read <int> ();
    	for(int i = 1; i <= n; i++)
    	    f[0][0][i] = f[1][0][i] = a[i] = read <int> (); 
    	getst(); 
    	for(int i = 1; i + L - 1 <= n; i++)
    	    res = max(res, 1.0 * (query(1, i, i + L - 1) - query(0, i, i + L - 1)) / (L - 1 + m)); 
    	double l = 0, r = 1000, mid;
    	while(fabs(r - l) > eps)
    	{
    	    mid = (l + r) / 2;
    	    if(check(mid)) ans = mid, l = mid;
    	    else r = mid; 
    	}
    	res = max(res, ans); 
    	reverse(a + 1, a + n + 1); 
    	l = 0, r = 1000; 
    	while(fabs(r - l) > eps)
    	{
    	    mid = (l + r) / 2;
    	    if(check(mid)) ans = mid, l = mid;
    	    else r = mid; 
    	}
    	printf("%.4lf
    ", max(res, ans)); 
        }
        return 0; 
    }
    
  • 相关阅读:
    Managing C++ Objects: 管理C++对象 —— 一些建议准则
    像Java一样管理对象:T&形式仅仅用在参数传递
    Visual Studio的语法着色终于调得赏心悦目
    j.u.c: Java并发包的5大块
    笔记:Java Language Specification
    线程与锁
    分布式系统涉及的基本问题
    微服务为什么是一种趋势
    js实现复制功能
    css label两端对齐
  • 原文地址:https://www.cnblogs.com/ztlztl/p/12358570.html
Copyright © 2020-2023  润新知