Subsequence
题意
- 给定一个长度为 n n n 的序列,给定一个范围 [ m , k ] [m, k] [m,k]。我们需要找到一个最长的连续子序列,满足这个子序列中最大值和最小值的差值在给定的范围 [ m , k ] [m, k] [m,k] 内,输出满足条件的最长连续子序列的长度。
思路
- 很显然,对于某个位置
i
i
i 作为左端点的连续子序列,其最大值和最小值的差值是单调不减的。所以对于原始序列,我们维护一个单调增队列
I
N
C
INC
INC 和一个单调减队列
D
E
C
DEC
DEC。显然我们要找的最值的差值就是
d
i
f
=
D
E
C
.
f
r
o
n
t
−
I
N
C
.
f
r
o
n
t
dif = DEC.front - INC.front
dif=DEC.front−INC.front。对于这个差值,实际上只有三种情况:
(1)在范围内,更新答案并保留队列
(2)dif < m,保留队列
(3)dif > k,删掉单调增队列和单调减队列中队首元素位置比较靠前的那个,由此保证差值不会越来越大。
在这个过程中,我们只需要记录更新不合法的位置 $lst,更新答案时即是 i − l s t i-lst i−lst. lst初始化为0,然后如果出现了dif > k 的情况,那么就更新lst为踢掉的元素的位置。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxN = 1000006;
int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -f; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n, m, k;
struct que{
int a[maxN];
int id[maxN];
int head, rear;
}INC, DEC;
int main() {
while(~scanf("%d", &n)) {
m = read(), k = read();
INC.head = DEC.head = 0;
INC.rear = DEC.rear = -1;
int ans = 0, lst = 0;
for(int i = 1; i <= n; ++ i ) {
int num = read();
while(INC.head <= INC.rear && INC.a[INC.rear] > num) -- INC.rear;
INC.a[++ INC.rear] = num;
INC.id[INC.rear] = i;
while(DEC.head <= DEC.rear && DEC.a[DEC.rear] < num) -- DEC.rear;
DEC.a[++ DEC.rear] = num;
DEC.id[DEC.rear] = i;
int dif = DEC.a[DEC.head] - INC.a[INC.head];
if(dif >= m && dif <= k) {
ans = max(ans, i - lst);
} else if(dif > k) {
if(INC.id[INC.head] < DEC.id[DEC.head]) lst = INC.id[INC.head ++ ];
else lst = DEC.id[DEC.head ++ ];
}
}
printf("%lld\n", ans);
// fflush(stdout);
}
return 0;
}
一些样例
5 0 1
5 4 1 3 2
ans: 2
6 2 3
4 5 4 3 1 2
ans: 4
4 2 3
4 4 5 4
ans: 0
10 5 10
165 15 1 11 45 32 15 154 51 5
ans: 2
10 2309 49430
43902 24903 420582 349809 23 398420 389 2839 8239 2098
ans: 4
6 2309 49430
23 398420 389 2839 8239 2098
ans: 4