首页 > 其他分享 >[题解] [洛谷P1404] 平均数

[题解] [洛谷P1404] 平均数

时间:2024-04-11 19:23:36浏览次数:18  
标签:子串 洛谷 int 题解 复杂度 扩展 P1404 枚举 平均数

洛谷P1404 平均数

题目描述

给一个长度为 \(n\) 的数列,我们需要找出该数列的一个子串,使得子串平均数最大化,并且子串长度 \(\geq m\) 。

输入格式

第一行两个整数 \(n\) 和 \(m\)。

接下来 \(n\) 行,每行一个整数 \(a_i\),表示序列第 \(i\) 个数字。

输出格式

一个整数,表示最大平均数的 \(1000\) 倍,如果末尾有小数,直接舍去,不要用四舍五入求整。

数据规模

对于 \(60\%\) 的数据,保证 \(m \leq n \leq 10^4\);
对于 \(100\%\) 的数据,保证 \(1 \leq a_i \leq 2000\)。

题解

朴素的思想是枚举所有的子串,找到最大平均数,时间复杂度 \(O(n^2)\),无法通过此题。
考虑优化,本题的特殊限制条件是子串长度不得小于 \(m\) 。如何保证这一条件满足呢?只需要先找到所有长度为 \(m\) 的子串,再在这些子串的基础上向前或向后扩展即可得到所有符合要求的子串。通过枚举子串的第一个元素的位置 \(i\) 易得子串的最后一个元素位置为 \(i + m - 1\),故可方便的在 \(O(n)\) 的复杂度内枚举长度为 \(m\) 的子串。
考虑扩展子串,如果向后扩展的同时向前扩展,则向后扩展的子串会和后续子串向前扩展的子串重合,形成枚举的重复,故只向前或向后扩展。此处采用向前扩展。优化的关键是在 \(O(\log n)\) 的复杂度内求出最优的扩展方法。显然枚举的复杂度是 \(O(n)\) 不符合要求。笔者的最初想法是使用前缀的最大平均值进行扩展,只需要 \(O(1)\) 的时间复杂度。但对于单个最高的数来说,可能选择多个较小的数的情况更优,因为两组数的并的平均值不等于两组数平均值的平均值,故作罢。那么还有什么方法可以过滤没有必要的扩展呢?
考虑二分答案。对于一个选定的基准数作为平均数的情况考虑扩展,易得以下扩展策略:

  1. 前缀的平均数小于基准数,舍弃前缀;
  2. 前缀的平均数大于等于基准数,则保留前缀。

因此,只需要在枚举长度为 \(m\) 的子串的同时用以上策略扩展即可得到基准数确定时的最优情况。二分的时间复杂度为 \(O(\log a)\),验证的时间复杂度为 \(O(n)\), 总时间复杂度为 \(O(n \log a)\) ,可以解决此题。

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;

const int MAXN = 1e6 + 3;

int n, m;
double a[MAXN];
double sum = 0.0; //维护只取m个数的和

bool check(double x) {
    sum = 0.0;
    for (int i = 1; i <= m; i++) {
        sum += a[i];
    }
    double ans = 0.0;
    double preSum = 0.0; //维护往前延伸的最大平均数的和(分子)
    double preNum = 0.0; //维护往前延伸的最大平均数的数量(分母)
    for (int i = 1; i <= n - m + 1; i++) { //从m开始枚举子串的第一个数的位置
        double preAvg = 0.0; //往前延伸的最大平均数
        if (preNum != 0) preAvg = preSum / preNum;
        if (preAvg < x) preSum = a[i - 1], preNum = 1.0; //之前的平均数达不到x,则只保留新出现的数
        else preSum += a[i - 1], preNum += 1.0; //否则在保留之前的前缀的基础上加上新出现的数
        ans = max(ans, max((sum + preSum) / ((double)m + preNum), sum / (double)m)); //更新答案
        sum -= a[i]; //减去子串的第一个数
        sum += a[i + m]; //加上新出现的一个数
    }
    return ans >= x;
}

signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    double l = 0, r = 2000, mid;
    while (l + 1e-5 < r) {
        mid = (l + r) / 2.0;
        if (check (mid)) l = mid;
        else r = mid;
    }
    cout << (int)(r * 1000.0) << '\n';
    return 0;
}

标签:子串,洛谷,int,题解,复杂度,扩展,P1404,枚举,平均数
From: https://www.cnblogs.com/Floyd3265/p/18129906

相关文章

  • [题解] <NOIP2017> 时间复杂度
    [题解]NOIP2017时间复杂度题目描述小明正在学习一种新的编程语言A++,刚学会循环语句的他激动地写了好多程序并给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序,于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正......
  • 洛谷题单指南-数学基础问题-P1072 [NOIP2009 提高组] Hankson 的趣味题
    原题链接:https://www.luogu.com.cn/problem/P1072题意解读:求有多少个x,满足x和a0​的最大公约数是a1​,x和b0​的最小公倍数是b1,多组数据。解题思路:枚举法:因为x和a0​的最大公约数是a1​,x和b0​的最小公倍数是b1,所以x不大于b1​。枚举x有两种思路:1、x是a1的倍数,最多需要枚举......
  • CF617E XOR and Favorite Number 题解
    想了好久才明白zz来源?:莫队题单题目大意给定一个长度为\(n\)的序列\(a\),然后再给一个数字\(k\),再给出\(m\)组询问,每组询问给出一个区间,求这个区间里面有多少个子区间的异或值为\(k\)。\(1\len,m\le10^5\)\(0\lek,a_i\le10^6\)\(1\lel_i,r_i\len\)题......
  • 排序规则冲突问题解决
    --英文操作系统数据库恢复到中文版本操作系统的时候容易出现一下问题--无法解决equalto运算中"SQL_Latin1_General_CP1_CI_AS"和"Chinese_PRC_CI_AS"之间的排序规则冲突。--简单解决办法如下:指定排序规则COLLATESQL_Latin1_General_CP1_CI_AS或者Chinese_PRC_CI_AS......
  • 洛谷题单指南-数学基础问题-P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题
    原题链接:https://www.luogu.com.cn/problem/P1029题意解读:已知x,y,求有多少对p、q,使得p、q的最大公约数为x,最小公倍数为y。解题思路:枚举法即可。枚举的对象:枚举p,且p必须是x的倍数,还有p<=yq的计算:q=x*y/p,q要存在,必须x*y%p==0,且gcd(p,q)==x100分代码:#include......
  • 洛谷题单指南-数学基础问题-P1835 素数密度
    原题链接:https://www.luogu.com.cn/problem/P1835题意解读:要计算L-R范围内素数的个数。解题思路:直接对L~R的每个数判断素数肯定不可取,因为L、R的范围较大。既然要计算素数的个数,那么可以把其中的合数标记出来即可。如何标记合数?可以借助于筛素数的算法思想,枚举每一个素数,然......
  • 【题解】CF311E Biologist题解
    CF311EBiologist题解非常好的一道最小割。思路首先看到每一个位置又会有\(01\)两种情况,然后要满足一些要求,求最大收益,考虑类似于P4313文理分科和P1361小M的作物这种集合划分的建图方法,也就是要用最小割求解。由于我们要求的是最大收益,所以我们要先明确要最小化什么,......
  • 【题解】CF1187G Gang Up
    【题解】CF1187GGangUp题意给定一个图,有\(k\)个人要走到\(1\)号节点,问最小花费。解法一眼丁真,鉴定为费用流。考虑到这道题花费会与时间有关,所以分层图,启动!。按时刻分层,现在分析每个人在第\(k\)时刻的动向:1.呆着不动。2.走到下一个节点。对于动向\(1\),从时......
  • 洛谷 P6692 题解
    洛谷P6692出生点题意简述\(n\)行\(m\)列构成\(nm\)个格点,在其中指定\(k\)个障碍点。每行、每列之间的距离为\(1\),每次任意选取两个非障碍点,计算这两个点的曼哈顿距离,求所有选法的距离之和。分析由容斥原理,答案为「任意两点之间的距离之和」\(-\)「每个障碍点到其他......
  • #莫队二次离线,根号分治#洛谷 5398 [Ynoi2018] GOSICK
    题目\(m\)组询问求\(\sum_{l\leqi,j\leqr}[a_i\bmoda_j==0],n,m,a_i\leq5\times10^5\)分析设\(f(l,r,x)\)表示\(i或j\in[1,x],i或j\in[l,r]\)时的答案,\(g_x\)表示\([1,x]\)的答案,根号的做法可以通过三秒由于涉及区间内的求值,需要在莫队的基础上二次离线,那......