题意:给定一个数字序列,定义ave(i,j)为从i到j的数字的平均值,要求j-i+1>=k,求最大的平均值。
解析:这个题可以转换一下,将sum[i]作为纵座标的值,那么就是求相距大于k的两点之间的斜率的最大值。(参考这篇文章:)可以知道加点的时候维护一个下凸的曲线,每次找出该点和曲线的切线即在该点的最大斜率。求斜率的时候,根据下凸曲线的性质,可以删去已找到的切点之前的点。
(吐槽一下:这题很卡输入,输入外挂都不管用,很恶心。看了评论区大神用fread的做法才过的)
代码(参考的大神代码):
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005;
const int MT = 25000000;
char IO_BUF[MT];
char *buf = IO_BUF;
int sum[N], Q[N], tol;
inline void read(int &a){
for(a = 0; *buf < 48;buf++);
while(*buf > 47) a = a*10+*buf++-48;
}
bool useless(ll x, ll y, ll z){
return 1LL*(sum[y]-sum[x])*(z-y) >= 1LL*(sum[z]-sum[y])*(y-x);
}
int main(){
tol = fread(IO_BUF, 1, MT, stdin);
int n, k;
double ans;
while(1){
if(buf - IO_BUF + 1 >= tol)break;
read(n);read(k);
sum[0] = 0;
ans = 0;
for(int i = 1; i <= n; i++){
read(sum[i]);
sum[i] += sum[i-1];
}
int head = 0, rear = -1;
for(int i = k; i <= n; i++){
while(head < rear && useless(Q[rear-1], Q[rear], i-k))
rear --;
Q[++rear] = i - k;
while(head < rear && useless(Q[head+1], Q[head], i))
head ++;
ans = max(ans, 1.0*(sum[i]-sum[Q[head]])/(i-Q[head]));
}
printf("%.2f\n", ans);
}
return 0;
}