P1419 寻找段落
首先二分答案,即:二分最大平均值。
我们将a全部减去mid,问题转化为判断是否存在一个长度在s~t范围内的区间它的和为正,如果有说明还有更大的平均值。
用前缀和和单调队列维护。
不会单调队列的点这里
然后用单调队列求出sum[i]-min(sum[i-t]~sum[i-s]),
然后判断是否大于0即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ifstream in("input.txt");
ofstream out("output.txt");
#define debug(x) cout<<"# "<<x<<endl
const ll N=100005;
const ll base=137;
const ll mod=2147483647;
const ll INF=1<<30;
ll n,m,a[N],s,t;
double sum[N],ans;
bool check(double mid)
{
for(int i=1;i<=n;++i)
sum[i]=sum[i-1]+(double)a[i]-mid;
ll head=1,tail=0,q[N];
for(int i=s;i<=n;++i)
{
while(head<=tail&&sum[q[tail]]>=sum[i-s])//如果>=说明队列里出现了降序
tail--;//把最前面的丢掉直到队列保持升序为止
q[++tail]=i-s;
//while(!q.empty()&&q.front.index<i-t)q.pop_front();
while(head<=tail&&q[head]<i-t)//队列里的元素多于最大上限t就pop掉最先进入的值
head++;
if(head<=tail&&sum[i]-sum[q[head]]>=0)//如果队列里区间和>=0说明还有更大的平均值(前缀和嘛减后的值就是区间和)
return 1;
}
return 0;
}
int main()
{
scanf("%lld",&n);
scanf("%lld %lld",&s,&t);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
double l=-10000.0,r=10000.0;
while(r - l >= 1e-4)
{
double mid=(l+r)/2.0;
if(check(mid))ans=mid,l=mid;
else r=mid;
}
printf("%.3f\n",ans);
return 0;
}
有任何疑问欢迎评论哦虽然我真的很菜