P1404 平均数
题目描述
给一个长度为n的数列,我们需要找出该数列的一个子串,使得子串平均数最大化,并且子串长度>=m。
输入格式
N+1行,
第一行两个整数n和m
接下来n行,每行一个整数a[i],表示序列第i个数字
输出格式
一个整数,他是最大平均数的1000倍,如果末尾有小数,直接舍去,不要用四舍五入求整。
输入输出样例
输入 #1
10 6 6 4 2 10 3 8 5 9 4 1
输出 #1
6500
说明/提示
【数据范围】
60% M<=N<=10000
100% M<=N<=100000 0<=a[i]<=2000
思路
先求部分和S(x),然后连续子序列平均值就转化为S-x平面上的斜率:ave(x,y)=(S(y)-s(x-1))/(y-x+1)。考虑x<y<z的三个点如果S(y)是上凸的,则这个点一定没贡献。所以有用的点构成一个下凸的折线,用一个队列维护这个折线,加入新点时(如当前点为i,则新点为i-m),如果与队尾2个点形成上凸,则删除队尾点。如果队首2个点与当前点形成上凸,同理删除队首点。最后每次队首元素都是与点i斜率最大的点,再求最值就行了
代码:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=100010; double ans; int q[N],t,h; int n,m,a[N]; double k(int x,int y) { return (a[y]-a[x]+0.0)/(y-x); } int main () { scanf("%d%d",&n,&m); for(int i=1,x; i<=n; i++) { scanf("%d",&x); a[i]=a[i-1]+x; } for(int i=m; i<=n; i++) { while(t-h>=2&&k(i-m,q[t-1])<k(i-m,q[t-2])) t--; q[t++]=i-m; while(t-h>=2&&k(i,q[h])<k(i,q[h+1])) h++; ans=max(ans,k(i,q[h])); } printf("%d\n",(int)floor(ans*1000)); return 0; }