最长连续子序列(贪心)
题意
给定一个长度为\(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;
}