luoguP1419 寻找段落(二分答案+单调队列)单调队列DP求在区间[l,r] 中长度至少为 m 的最大平均值

模板:单调队列DP求在区间\([l,r]\) 中长度至少为 \(m\) 的最大平均值

题目链接:Here

题意

给定一个长度为 \(n\) 的序列 \(a_1\) ~ \(a_n\) ,从中选取一段长度在 \(s\)\(t\) 之间的连续一段使其平均值最大。\((n<=100000)\)

分析

二分答案平均值。

\(a[i]-mid\) 中找到一段合法的串使其权值和最大。

当最大权值和大于等于 \(0\) 时则 \(mid\) 上移。

求最大权值和用单调队列就行。(预处理 \(a[i]-mid\) 的前缀和 \(sum[i]\)

Show Code

const int N = 1e5 + 10;
int n, s, t;
double a[N], sum[N];
int q[N];
bool check(double x) {
    for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + (a[i] - x);
    sum[0] = 0;
    int hh = 1, tt = 0;
    memset(q, 0, sizeof(q));
    for (int i = s; i <= n; ++i) {
        while (hh <= tt and sum[q[tt]] > sum[i - s]) tt--;
        q[++tt] = i - s;
        while (hh <= tt and q[hh] < i - t) ++hh;
        if (hh <= tt and sum[i] - sum[q[hh]] >= 0) return true;
    }
    return false;
}
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    cin >> n >> s >> t;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    double l = -10000, r = 10000;
    while (r - l > 1e-5) {
        double mid = (l + r) / 2.0;
        if (check(mid)) l = mid;
        else r = mid;
    }
    cout << fixed << setprecision(3) << l;
}

posted @ 2021-07-26 22:02  RioTian  阅读(90)  评论(0编辑  收藏  举报