平均数
题目描述
给一个长度为n的数列,我们需要找出该数列的一个子串,使得子串平均数最大化,并且子串长度>=m。
输入输出格式
输入格式:
N+1行,
第一行两个整数n和m
接下来n行,每行一个整数a[i],表示序列第i个数字
输出格式:
一个整数,他是最大平均数的1000倍,如果末尾有小数,直接舍去,不要用四舍五入求整。
输入输出样例
说明
【数据范围】
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; }