[题解] [JSOI2015] 送礼物

题面

题解

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

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

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

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

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

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

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

\[\displaystyle\begin{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\n", max(res, ans)); 
    }
    return 0; 
}
posted @ 2020-02-24 20:16  ztlztl  阅读(153)  评论(0编辑  收藏  举报