最长连续子序列(贪心)

题意

给定一个长度为\(n\)的整数序列\(a_1, a_2, \dots , a_n\)

现在,请你找到一个序列\(a\)的连续子序列\(a_l,a_{l+1}, \dots ,a_r\),要求:

  • \(\sum\limits_{i=l}^r a_i > 100 \times (r−l+1)\)
  • 连续子序列的长度(即\(r−l+1\))尽可能大。

请你输出满足条件的连续子序列的最大可能长度。

题目链接:https://www.acwing.com/problem/content/4490/

数据范围

\(1 \leq n \leq 10^6\)

思路

第一个条件要求均值大于\(100\),因此可以按照常规套路将每个元素减去\(100\),条件就转化为了连续子序列和大于\(0\)

如何快速判断连续子序列和是否大于\(0\)呢?可以考虑前缀和,记为\(s\)。若\(s_j > s_i\)\(i < j\),则\(a[i,j]\)的连续子序列和大于\(0\)

问题就转化为了,找到最大的\(j - i\),满足\(s_j > s_i\)

对于这种问题,我们可以通过排序固定一个条件,这样就只需要看一个条件了。在这里,按照\(s\)从小到大排序(注意每个元素初始下标要记录下来),如果\(s_i = s_j\)则初始下标大的放在前面。

扫描排序后的序列,维护前缀初始下标最小值,如果当前元素的初始下标大于前缀初始下标最小值,则更新答案。(这样满足了后面的一定比前面的大,如果两者相等是不会更新答案的,因为下标大的在前)。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 1000010;

int n;

struct Node
{
    ll num;
    int pos;
    
    bool operator < (const Node &t) const
    {
        if(num == t.num) return pos > t.pos;
        return num < t.num;
    }
}a[N];

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        ll x;
        scanf("%lld", &x);
        x -= 100;
        a[i].num = a[i - 1].num + x;
        a[i].pos = i;
    }
    a[0].num = 0, a[0].pos = 0;
    sort(a, a + n + 1);
    int ans = 0;
    int min_pos = n;
    for(int i = 0; i <= n; i ++) {
        if(a[i].pos > min_pos) ans = max(ans, a[i].pos - min_pos);
        min_pos = min(min_pos, a[i].pos);
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2022-06-27 11:35  pbc的成长之路  阅读(54)  评论(0编辑  收藏  举报