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;
}
posted @ 2021-01-23 23:08  kjd123456  阅读(97)  评论(0编辑  收藏  举报