子序列问题

自己出了一道题,感觉挺有意思的。

题目

一个长度为\(n\)的序列,将每个子序列的权值定义为\(\min(a-l,a_{l+1},...,a-r)\times \max(a-l,a_{l+1},...,a-r)\)。 对于所有长度不小于\(k\)的连续子序列,要你求出一段权值严格次大的子序列。如果不存在这样的子序列,输出最大的权值。

题解

因为总是会有一个数作为最小值,所以直接枚举每个数作为区间最小值。
当某个数\(a-i\)做最小值时,用单调栈分别算出\(a-i\)最多向左和向右延伸的最长长度。记为\(L-i\)\(R-i\)
如果\(R-i-L-i+1\geq k\),那么就可以把\(a-i\times max(a-l,a_{l+1},...,a-r)\)记录下来,因为它有可能成为答案。把\(max(a-l,a_{l+1},...,a-r)\)记为\(MAX\)
但是要求次大值。当\(a-i\)作为最小值时,需要找到一个区间\([l-0,r-0]\)使\(max(a[l-0] \sim a[r-0])\)\(MAX\)不同。并且\(r-0-l-0\)尽量大,那么就找到了这个次大值。具体方法是开桶记录每个元素的位置,查出与\(a-i\)相邻的两个\(MAX\)的位置,与\([L-i,R-i]\)取一个交集就是\([l-0,r-0]\)了。一样的方法把次大的权值记录下来。
处理完之后就能得到次小或者最大的权值了,注意细节就可以。
时间复杂度\(\mathjaxcal{O}(nlogn)\),主要瓶颈是找区间最大值上。

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e6 + 5;
int n, k, a[N], l[N], r[N], q[N], f[N][21], bit[N];
vector<int> v[N];
ll max1, max2;

int read() {
char ch = getchar();
int w = 0;
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') {
w = w * 10 + ch - 48;
ch = getchar();
}
return w;
}
void pre() {
bit[0] = 1;
for(int i = 1; i <= 20; i++) bit[i] = bit[i - 1] * 2;
int logn2 = (int)(log(n) / log(2));
for(int i = 1; i <= n; i++) f[i][0] = a[i];
for(int j = 1; j <= logn2; j++)
for(int i = 1; i <= n - bit[j] + 1; i++) {
f[i][j] = max(f[i][j - 1], f[i + bit[j-1]][j-1]);
}
int head = 1, tail = 0;
for(int i = 1; i <= n; i++) {
while(head <= tail && a[q[tail]] >= a[i]) tail--;
if(tail < head) l[i] = 1;
else l[i] = q[tail] + 1;
q[++tail] = i;
}
tail = 0;
for(int i = n; i >= 1; i--) {
while(head <= tail && a[q[tail]] >= a[i]) tail--;
if(tail < head) r[i] = n;
else r[i] = q[tail] - 1;
q[++tail] = i;
}
}
int query(int l, int r) {
int logn2=(int) (log(r - l + 1) / log(2));
return max(f[l][logn2], f[r - bit[logn2] + 1][logn2]);
}
void update(ll x) {
if(x > max1) {
max2 = max1;
max1 = x;
} else if(x < max1 && x > max2) {
max2 = x;
}
}

int main() {
n = read(); k = read();
for(int i = 1; i <= n; i++) {
a[i] = read();
v[a[i]].push-back(i);
}
pre();
for(int i = 1; i <= n; i++) {
if(r[i] - l[i] + 1 < k) continue;
int MAX = query(l[i], r[i]);
update(MAX * 1ll * a[i]);
if(a[i] == MAX) continue;
int y = lower-bound(v[MAX].begin(), v[MAX].end(), i) - v[MAX].begin(), l0, r0;
if(y == v[MAX].size()) r0 = n;
else r0 = v[MAX][y] - 1;
--y;
if(v[MAX][y] == i) --y;
if(y < 0) l0 = 1;
else l0 = v[MAX][y] + 1;
l0 = max(l0, l[i]); r0 = min(r0, r[i]);
if(r0 - l0 + 1 < k || r0 < i || l0 > i) continue;
update(query(l0, r0) * 1ll * a[i]);
}
if(!max2) printf("%lld\n", max1);
else printf("%lld\n", max2);
return 0;
}

子序列问题
https://widsnoy.top/posts/2e75/
作者
widsnoy
发布于
2020年9月1日
许可协议