其他分享
首页 > 其他分享> > P1419 寻找段落

P1419 寻找段落

作者:互联网

寻找段落

给定序列 \(A\),求所有长度在 \([L,R]\) 中的连续序列的平均值的最大值。

很早就接触过这道题的 二分答案 + 单调队列 解法,好像是在 lyd 老师的蓝书上,那是 \(O(n\log n)\) 的。

当时认为那种写法十分优美,但随着知识积累发现可以做到更加优美的 \(O(n)\) 复杂度,还是挺感慨的。

首先套路的做一个前缀和,那么我们就是求:

\[\max\limits_{L\leq i-j+1\leq R}\{\frac{S_i-S_{j-1}}{i-(j-1)}\} \]

这个形式很像斜率优化得到的式子,但是其中根本没有未知项,所以这个就是一个简化的斜率优化。(其实就是求最大的斜率)

维护斜率的最大值,不难发现加入的点 \((x,y)\) 的 \(x,y\) 都是严格递增的,所以显然需要维护一个下凸壳。

然后也不难观察得到每次需要在队首找到使 \(k\) 取得 \(\max\) 的点, 而前面的点就没有用了。

即在队首更新答案,在队尾插入维护下凸壳,还是挺标准的单调队列呢。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 100010;
int n, L, R, l, r, a[N], s[N], q[N], X[N], Y[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

void Push(int x, int y){
	while(l < r && 1LL * (Y[r] - Y[r - 1]) * (x - X[r]) >= 1LL * (y - Y[r]) * (X[r] - X[r - 1]))
		r --;
	q[++ r] = x + 1, X[r] = x, Y[r] = y;
}

double Query(int x, int y){
	while(l < r && 1LL * (y - Y[l]) * (x - X[l + 1]) <= 1LL * (y - Y[l + 1]) * (x - X[l]))
		l ++;
	return (double)(y - Y[l]) / (double)(x - X[l]);
}

int main(){
	n = read(); L = read(), R = read();
	for(int i = 1; i <= n; i ++){
		a[i] = read();
		s[i] = s[i - 1] + a[i];
	}
	l = 1, r = 0;
	double ans = 0.0;
	for(int i = L; i <= n; i ++){
		Push(i - L, s[i - L]);
		while(l <= r && q[l] <= i - R) l ++;
		ans = max(ans, Query(i, s[i]));
	}
	printf("%.3lf\n", ans);
	return 0;
}

标签:P1419,段落,leq,int,队首,寻找,斜率,while,include
来源: https://www.cnblogs.com/lpf-666/p/15027759.html