Loading

POJ 2018. Best Cow Fences

有关数据

\(\texttt{Time Limit}\) \(\texttt{Memory Limit}\) \(\texttt{Difficulty}\)
\(\color{green}{\texttt{1 sec}}\) \(\color{red}{\texttt{30000 K}}\) \(\color{blue}{\frac{31}{100}}\)

题目大意

给定长度为 \(N\) 正整数数列 \(A\), 求一个平均数最大的、长不小于 \(L\) 的(连续的)子段。

提示

  • \(1 \leq N \leq 10^5\)
  • \(0 \leq L \leq N\)
  • \(-N \leq A_i \leq N\)

解法分析

二分答案,将问题转换为是否存在一个平均数小于 \(\texttt{mid}\) 且长度不小于 \(L\) 的子段
如果把序列上的每个数都减去 \(\texttt{mid}\),则问题转化成寻找和大于等于 \(0\) 且长度不小于 \(L\) 的子段。
考虑 \(O(n^2\log{n})\) 的做法。我们需要求出所有长度符合条件的子段中和最大的,存不存在全看这个最大的和是否非负。(很容易理解吧)那么,枚举字段左右区间,答案就是 \(\max\limits_{L\leq i \leq N}\{sum_i-\min\limits_{0\leq j \leq i - L}\{sum_j\}\}\)
显然时间复杂度不允许。注意到左边的 \(\min\limits_{0\leq j \leq i - L}\{sum_j\}\) 可以在第一层循环 \(i\) 里递推求出。
所以,我们就可以 \(O(n)\) 解决 \(\texttt{check}\) 的问题。至此,整道题就解决了。

AC Code

# include <iostream>
using namespace std;

# define ll long long
# define lf double
# define int ll
# define GO(i,a,b) for(ll i = a; i <= b; i ++)
# define RO(i,b,a) for(ll i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a, b)
# define PB push_back
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

CI maxn = 1e6 + 7;

int n, k;
lf a[maxn]; 
lf b[maxn];
lf sum[maxn];

signed main(){
	cin >> n >> k;
	GO (i, 1, n) scanf("%lf", &a[i]);
	lf l = -1e6, r = 1e6;
	lf eps = 1e-5;
	while (r - l > eps){
		lf mid = (l + r) / 2.0;
		GO (i, 1, n) b[i] = a[i] - mid;
		GO (i, 1, n) sum[i] = sum[i - 1] + b[i];
		lf chk = -2e18;
		lf min_val = 2e18;
		GO (i, k, n){
			min_val = min(min_val, sum[i - k]);
			chk = max(chk, sum[i] - min_val);
		}
		if (chk >= 0){
			l = mid;
		}
		else{
			r = mid;
		}
	}
	cout << (int)(r * 1000);
	return 0;
}
posted @ 2023-03-18 16:49  DE_aemmprty  阅读(34)  评论(0)    收藏  举报