洛谷P1419 寻找段落 题解

题目传送门。

首先发现最大段落平均数具有单调性,首先二分,然后发现对于每个 \(mid\),只需要找到一个长度在 \([S,T]\) 范围内的子段并且这个子段的平均值大于等于 \(mid\) 即可,转化一下,只需要重构一个序列 \(b\)\(b_i = a_i-mid(1 \le i \le n)\),然后求一下前缀和数组 \(sum\),就变成了求两个数 \(i,j(1 \le j \le i \le n,S \le i-j+1 \le T)\),使得 \(sum_i-sum_{j-1} \ge mid\),再次转化,变成求两个数 \(i,j(0 \le j < i \le n,S \le i-j \le T)\),使得 \(sum_i-sum_{j} \ge mid\),相当于直接把 \(j\) 变成 \(j-1\) 了,这样更方便,然后继续转化,变成求两个数 \(i,j(0 \le j < i \le n,i-T \le j \le i-S)\),使得 \(sum_i-sum_{j} \ge mid\),那么,我们可以单调队列,对于每个 \(i(i \ge S)\),往里加下标的不再是 \(i\),而是 \(i-S\),然后每次下标小于 \(i-T\)\(sum\) 值统统扔掉,然后对于每个 \(i(i \ge S)\),只需要看一下最小的 \(sum_j(i-T \le j \le i-S)\)\(sum_i\) 的差是不是大于 \(0\),如果是,说明这个平均数可行,否则则不行。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
const double eps = 1e-4;//此题1e-4够了,但是其它题不一定,一般都得设置成1e-5或1e-6,毒瘤题1e-9
double sum[N];
int a[N];
int q[N];
signed main()
{
    int n,S,T;
    scanf("%d %d %d",&n,&S,&T);
    for(int i = 1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    double l = -1e4,r = 1e4,ans = 0;
    while(r-l>eps)
    {
        double mid = (l+r)/2;
        sum[0] = 0;
        for(int i = 1;i<=n;i++)
        {
            sum[i] = sum[i-1]+a[i]-mid;
        }
        int h = 1,t = 0,flag = 0;
        for(int i = S;i<=n;i++)
        {
            while(h<=t&&sum[q[t]]>sum[i-S])
            {
                t--;
            }
            q[++t] = i-S;
            while(h<=t&&q[h]<i-T)
            {
                h++;
            }
            if(sum[i]-sum[q[h]]>=0)
            {
                flag = 1;
                break;
            }
        }
        if(flag)
        {
            ans = mid;
            l = mid;
        }
        else
        {
            r = mid;
        }
    }
    printf("%.3lf",ans);
    return 0;
}
posted @ 2025-05-03 11:12  林晋堃  阅读(31)  评论(0)    收藏  举报