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;
}

浙公网安备 33010602011771号