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

 

posted @ 2019-08-19 10:25  双子最可爱啦  阅读(127)  评论(0编辑  收藏  举报