题目大意
给定正整数序列 A,求一个平均数最大的,长度不小于 L 的(连续的)子段
解
为了方便计算所以所有数都乘上1000.
Then, 二分均值。
n n n 的范围到 1 0 5 10^5 105
考虑二分判断结果如何在 O ( n ) O(n) O(n) 或 O ( n l o g n ) O(nlogn) O(nlogn) 的复杂度内解决:
我们可以将所有数减去 mid ,然后累计前缀和。
当一段数 a i a_i ai ~ a j ( j < = i − l ) a_j (j <= i-l) aj(j<=i−l) 的和 S u m [ i ] − S u m [ j ] Sum[i]-Sum[j] Sum[i]−Sum[j] 大于0时,则符合条件。
我们可以使取值范围内的 S u m [ j ] Sum[j] Sum[j] 尽量小,遂可以在 O ( n ) O(n) O(n) 内解决拉。
代码
#include<cstdio>
int N, L;
long long l, r, mid, a[100011], s[100011], ls[100011];
bool check(long long k){
for(int i = 1; i <= N; ++i) //减去mid
ls[i] = s[i] - mid * i;
long long minn = 0;
for(int i = L; i <= N; ++i){
minn = ls[i-L] < minn? ls[i-L] : minn; //更新Sum_j最小值
if(ls[i] - minn >= 0) return 1; //有段数符合条件
}
return 0;
}
int main(){
scanf("%d%d", &N, &L);
for(int i = 1; i <= N; ++i){
scanf("%lld", &a[i]);
a[i] = a[i] * 1000; //方便计算
s[i] = s[i-1] + a[i];
r = a[i]>r ? a[i]:r;
}
l = s[L] / L;
while(l < r){
//二分
mid = (l + r + 1) >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
printf("%lld", l); //输出
}