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

有任何疑问欢迎评论哦虽然我真的很菜