【ybtoj】【二分】【例题3】最大均值

【例题3】最大均值


link

传送门
题目


解题思路

第一步,把这破小数踢掉,全部乘1000

二分平均值mid,先把A序列全部减去mid,那么就是求一个长度不小于L的子段,且子段和不为0即可

怎么求这个子段呢?
首先想到前缀和, O ( n 2 ) O(n^2) O(n2),明显不行
求以y结尾的最大子段, m a x ( s u m [ y ] − s u m [ j ] ) ( 1 < = j < = y − L ) max(sum[y] - sum[j])(1<=j<=y-L) max(sum[y]sum[j])(1<=j<=yL)
y是顺序的,那么j的可能性变少
也就是说以y结尾时答案由 s u m [ m i n j ] sum[minj] sum[minj]构成,以y+1结尾时,答案由 m i n ( s u m [ m i n j ] , s u m [ y + 1 − L ] ) min(sum[minj],sum[y+1-L]) min(sum[minj],sum[y+1L])构成
O ( n ) O(n) O(n)可行


Code

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const long long maxn = 9223372036854775807;
long long n, m, a[100100], l, r, mid, ans, minj, s[100100];

bool check(long long x) {
    
    
	memset(s, 0, sizeof(s));
	for (int i = 1; i <= n; i++)
		s[i] = s[i - 1] + (a[i] - x)//求出前缀和(序列和减去mid)
	long long minj = maxn, ans = -maxn;
	for (int i = m; i <= n; i++) {
    
    
		minj = min(minj, s[i - m]);//省去j的循环
		ans = max(ans, s[i] - minj);
	}
	return (ans >= 0);//答案非0
}

int main() {
    
    
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%lld", &a[i]);
		a[i] *= 1000;//抹去小数
		r += a[i];
	}
	while (l <= r) {
    
    
		mid = (l + r) / 2;
		if (check(mid)) {
    
    
			l = mid + 1;
			ans = mid;
		}else r = mid - 1;
	}
	printf ("%lld", ans);
}

猜你喜欢

转载自blog.csdn.net/qq_39940018/article/details/112114982