Luogu P1419 寻找段落
我又复活啦!!!
题目链接
题目描述
给定一个长度为\(n\)的序列\(a\),定义\(a_i\) 为第\(i\)个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在\([S, T]\)之间的连续序列。最有价值段落是指平均值最大的段落。
段落的平均值等于段落总价值除以段落长度。
输入格式
第一行一个整数\(n\),表示序列长度。
第二行两个整数\(T\)和\(T\),表示段落长度的范围,在\([S, T]\)之间。
第三行到第\(n+2\)行,每行一个整数表示每个元素的价值指数。
输出格式
一个实数,保留 33 位小数,表示最优段落的平均值。
输入 #1
3
2 2
3
-1
2
输出 #1
1.000
Solution
平均值的题目有很多都与二分有关系
答案具有二分性,试一下二分。
若\(s\)数组表示\(a\)数组的前缀和。\(mid\)表示二分中间值。
那么,我们就是要找到一个\(i\),\(j\)使\((s[i] - s[j]) / (i - j)) \ge mid,(i > j且S \le i - j \le T) \implies s[i] - mid * i \ge s[j] - mid * j\)。
令\(t[i] = s[i] - mid * i\),我们就是要找到一对\(i,j(i > j且S \le i - j \le T)\),使得\(t[i] > t[j]\)。这可以通过单调队列做到,参考luogu P1886。遍历每一个\(t[i]\),寻找\(min(t[i - S]...t[i - T])\)即可。
\(Code\)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
int que[MAXN],n,s,T,head,tail;
int a[MAXN];
double t[MAXN];
bool judge(double cur) {
head = 1;
tail = 0;
for (int i = 1; i <= n; i++) {
t[i] = a[i] - cur * i;
}
for (int i = 1; i <= n; i++) {
if (i >= s) {
while (tail >= head && t[i - s] < t[que[tail]]) {
tail--;
}
que[++tail] = i - s;
}
if (head <= tail && que[head] < i - T) head++;
if (t[i] >= t[que[head]] && head <= tail) return true;
}
return false;
}
int main() {
scanf("%d%d%d",&n,&s,&T);
for (int i = 1; i <= n; i++) {
scanf("%d",&a[i]);
a[i] += a[i - 1];
}
double l = -1e4,r = 1e4 + 10;
while (r - l > 0.00001) {
double mid = (l + r) / 2;
if (judge(mid)) l = mid;
else r = mid;
}
printf("%.3lf",l);
return 0;
}