POJ 2796 - Feel Good

题目链接:POJ - 2796

这是一个典型问题:需要你在短时间内求出所有的点左边/右边第一个比它大/小的位置。

应用:

方法一:利用已经得到的信息转移

以找到某个点左边/右边第一个比它小的位置为例(这里不包括那个比它小的点):

  • \(a(i) < a(l_i - 1)\) 时,\(l_i = l_{l_i - 1}\),直到满足 \(a(i) < a(l_i - 1)\)
  • \(a(i) > a(r_i + 1)\) 时,\(r_i = r_{r_i + 1}\),直到满足 \(a(i) > a(r_i + 1)\)

初始化\(a_0 = a_{n+1} = -\infty\),保证不会越界。

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
#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;
typedef long long LL;
const int MAX_N = 1000000 + 5;
int n, a[MAX_N], lNext[MAX_N], rNext[MAX_N];
LL pre[MAX_N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", a + i), pre[i] = pre[i - 1] + a[i];
a[0] = a[n + 1] = -INT_MAX;
for (int i = 1; i <= n; ++i) lNext[i] = rNext[i] = i;
for (int i = 1; i <= n; ++i) {
while (a[i] <= a[lNext[i] - 1]) lNext[i] = lNext[lNext[i] - 1];
}
for (int i = n; i >= 1; --i) {
while (a[i] <= a[rNext[i] + 1]) rNext[i] = rNext[rNext[i] + 1];
}
LL ans = -INT_MAX;
int ansL = -1, ansR = -1;
for (int i = 1; i <= n; ++i) {
if (ans < a[i] * (pre[rNext[i]] - pre[lNext[i] - 1])) {
ans = a[i] * (pre[rNext[i]] - pre[lNext[i] - 1]);
ansL = lNext[i]; ansR = rNext[i];
}
}
printf("%lld\n%d %d\n", ans, ansL, ansR);
return 0;
}

方法二:单调栈

维护一个单调栈,单调栈的顶部是左边/右边第一个比当前点小的点,如果需要不包含这个点,则左边+1右边-1即可。

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
#include <cstdio>
#include <stack>
#include <algorithm>
#include <climits>
using namespace std;
typedef long long LL;
const int MAX_N = 1000000 + 5;
int n, a[MAX_N], lNext[MAX_N], rNext[MAX_N];
LL pre[MAX_N];
stack <int> s;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", a + i), pre[i] = pre[i - 1] + a[i];
while (!s.empty()) s.pop();
for (int i = 1; i <= n; ++i) {
while (!s.empty() && a[s.top()] >= a[i]) s.pop();
lNext[i] = (s.empty() ? 1 : s.top() + 1);
s.push(i);
}
while (!s.empty()) s.pop();
for (int i = n; i >= 1; --i) {
while (!s.empty() && a[s.top()] >= a[i]) s.pop();
rNext[i] = (s.empty() ? n : s.top() - 1);
s.push(i);
}
LL ans = -INT_MAX;
int ansL = -1, ansR = -1;
for (int i = 1; i <= n; ++i) {
if (ans < a[i] * (pre[rNext[i]] - pre[lNext[i] - 1])) {
ans = a[i] * (pre[rNext[i]] - pre[lNext[i] - 1]);
ansL = lNext[i]; ansR = rNext[i];
}
}
printf("%lld\n%d %d\n", ans, ansL, ansR);
return 0;
}