题目描述
JYY和CX的结婚纪念日即将到来,JYY来到萌萌开的礼品店选购纪念礼物。萌萌的礼品店很神奇,所有出售的礼物都按照特定的顺序都排成一列,而且相邻的礼物之间有一种神秘的美感。于是,JYY决定从中挑选连续的一些礼物,但究竟选哪些呢?假设礼品店一共有N件礼物排成一列,每件礼物都有它的美观度。排在第i(1leq ileq N1≤i≤N)个位置的礼物美观度为正整数A_iAi。JYY决定选出其中连续的一段,即编号为礼物i,i+1,…,j-1,ji,i+1,…,j−1,j的礼物。选出这些礼物的美观程度定义为:
(M(i,j)-m(i,j))/(j-i+k)(M(i,j)−m(i,j))/(j−i+k),其中M(i,j)M(i,j)表示max{A_i,A_{i+1}....A_j}max{Ai,Ai+1....Aj},m(i,j)m(i,j)表示min{A_i,A_{i+1}....A_j}min{Ai,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; 所以若max{A[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 ", ans); } return 0; }