Evanyou Blog 彩带

  题目传送门

平均数

题目描述

给一个长度为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


  分析:

  一道01规划的题,但是之前都没有学过这玩意儿(太懒了),于是今天考试就暴力水了点分。。。

  依题目要求,我们先二分答案,令$mid$为要验证的中位数,然后就是判断是否存在一个长度不小于$m$的子序列和使得其平均数大于$mid$,但是显然这样的话很不方便,那么转化一下。判断的时候我们先用一个前缀和搞一下,令$s[i]=s[i-1]+(a[i]-mid)$,也就是说,$s[i]$是$a[i]-mid$的前缀和,那么我们也就可以知道,如果要令mid满足条件,实际上也就是要存在一段区间$[x,y]$使得$s[y]-s[x-1]>=0$,这个式子等价于$(sum a[i])-mid*(y-x+1),iin [x,y]$。而且我们可以知道如果当前区间的右端点为$y$,那么左端点可以是$[1,y-m+1]$中的任意一个,这样子的话我们对于一个右段点$y$,我们只需要看$[1,y-m+1]$中最小的那个值就可以了,也就是只看$sum[y]-min{sum[x]},xin [1,y-m+1]$是否大于零。

  Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=1e9;
const int N=1e5+7;
ll n,m,a[N],s[N],ans;
inline ll read()
{
    char ch=getchar();ll num=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
    num=num*10+ch-'0';ch=getchar();}
    return num;
}
inline bool check(ll mid)
{
    for(ll i=1;i<=n;i++)
    s[i]=s[i-1]+(a[i]-mid);
    ll last=0;
    for(ll i=m;i<=n;i++){
        if(s[i]-last>=0)return true;
        last=min(last,s[i-m+1]);}
    return false;
}
int main()
{
    n=read();m=read();
    ll l=0,r=-1;
    for(int i=1;i<=n;i++){
    a[i]=read(),a[i]*=1000;
    r=max(r,a[i]);}
    while(l<=r){
        ll mid=(l+r)>>1;
        if(check(mid))l=mid+1,ans=mid;
        else r=mid-1;}
    printf("%lld",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/cytus/p/9319427.html