[51Nod 1564 区间的价值]单调栈
1. 题目链接
2. 题目描述
3. 解题思路
首先,用单调栈求出每个位置 i i ,求出和 Rmini R m i n i ,其中:
- Lmini=min{j} L m i n i = m i n { j } , 其中 j j 满足都有 Ak≥Ai A k ≥ A i ;
- Rmini=max{j} R m i n i = m a x { j } , 其中 j j 满足都有 Ak≥Ai A k ≥ A i ;
然后,就可以枚举每个位置
i
i
, 尝试求出以第个元素为区间最小值时的所有区间的最大值乘最小值,即求出
max{aj}∗ai
m
a
x
{
a
j
}
∗
a
i
, 其中,
Lmini≤j≤Rmini
L
m
i
n
i
≤
j
≤
R
m
i
n
i
.但是复杂度依然是
O(n2)
O
(
n
2
)
.
而且,我们对于每个位置
i
i
,可以找到满足一个位置
pi∈[Lmini,Rmini]
p
i
∈
[
L
m
i
n
i
,
R
m
i
n
i
]
且
A[pi]=max{aj}
A
[
p
i
]
=
m
a
x
{
a
j
}
(其中Lmini≤j≤Rmini)
(
其
中
L
m
i
n
i
≤
j
≤
R
m
i
n
i
)
,那么可以知道的是,对于任意满足
Lmini≤j≤k≤Rmini且j≤i,pi≤k
L
m
i
n
i
≤
j
≤
k
≤
R
m
i
n
i
且
j
≤
i
,
p
i
≤
k
的
j,k
j
,
k
来说,区间
[j,k]
[
j
,
k
]
的区间价值都是
Api∗Ai
A
p
i
∗
A
i
.
又因为,随着区间长度的增加,最大价值的区间价值是递减的, 那就可以直接用对用
Api∗Ai
A
p
i
∗
A
i
直接更新长度在
[1,Rmini−Lmini+1]
[
1
,
R
m
i
n
i
−
L
m
i
n
i
+
1
]
区间的答案;又因为答案是单调的,可以用后缀来代替线段树进行区间更新了.
(Orz, 数学描述起来好费劲)
4. 参考代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const long double eps = 1e-8;
const ll infl = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 100005;
int n, A[MAXN];
ll res[MAXN];
int mi[30][MAXN], ma[30][MAXN];
int lmin[MAXN], rmin[MAXN];
int rmq_min(int l, int r) {
int w = r - l + 1, d = log2(w);
return min(mi[d][l], mi[d][r - (1 << d) + 1]);
}
int rmq_max(int l, int r) {
int w = r - l + 1, d = log2(w);
return max(ma[d][l], ma[d][r - (1 << d) + 1]);
}
int main() {
#ifdef __LOCAL_WONZY__
freopen("input-3.txt", "r", stdin);
#endif
scanf("%d", &n);
//n = 1e5;
for(int i = 1; i <= n; ++i) {
scanf("%d", &A[i]);
//A[i] = rand() + 1;
mi[0][i] = ma[0][i] = A[i];
res[i] = 0;
}
for(int j = 1; j < 30; ++j) {
int w = 1 << j;
for(int i = 1; i + w - 1 <= n; ++i) {
ma[j][i] = max(ma[j - 1][i], ma[j - 1][i + (w >> 1)]);
mi[j][i] = min(mi[j - 1][i], mi[j - 1][i + (w >> 1)]);
}
}
stack<int> stk;
for(int i = 1; i <= n; ++i) {
while(!stk.empty() && A[stk.top()] > A[i]) {
rmin[stk.top()] = i - 1;
stk.pop();
}
stk.push(i);
}
while(!stk.empty()) {
rmin[stk.top()] = n;
stk.pop();
}
for(int i = n; i >= 1; --i) {
while(!stk.empty() && A[stk.top()] > A[i]) {
lmin[stk.top()] = i + 1;
stk.pop();
}
stk.push(i);
}
while(!stk.empty()) {
lmin[stk.top()] = 1;
stk.pop();
}
for(int i = 1; i <= n; ++i) {
int w = rmin[i] - lmin[i] + 1;
res[w] = max(res[w], (ll) A[i] * rmq_max(lmin[i], rmin[i]));
}
for(int i = n; i >= 1; --i) res[i] = max(res[i], res[i + 1]);
for(int i = 1; i <= n; ++i) printf("%lld\n", res[i]);
return 0;
}