[51Nod 1564 区间的价值]单调栈

[51Nod 1564 区间的价值]单调栈

1. 题目链接

[51Nod 1564 区间的价值]

2. 题目描述

题意描述

3. 解题思路

首先,用单调栈求出每个位置 i i ,求出Lmini Rmini R m i n i ,其中:

  • Lmini=min{j} L m i n i = m i n { j } , 其中 j j 满足k[j,i]都有 AkAi A k ≥ A i ;
  • Rmini=max{j} R m i n i = m a x { j } , 其中 j j 满足k[i,j]都有 AkAi A k ≥ A i ;

然后,就可以枚举每个位置 i i , 尝试求出以第i个元素为区间最小值时的所有区间的最大值乘最小值,即求出 max{aj}ai m a x { a j } ∗ a i , 其中, LminijRmini L m i n i ≤ j ≤ R m i n i .但是复杂度依然是 O(n2) O ( n 2 ) .
而且,我们对于每个位置 i i ,可以找到pi满足一个位置 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 } (LminijRmini) ( 其 中 L m i n i ≤ j ≤ R m i n i ) ,那么可以知道的是,对于任意满足 LminijkRminiji,pik 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 ] 的区间价值都是 ApiAi A p i ∗ A i .
又因为,随着区间长度的增加,最大价值的区间价值是递减的, 那就可以直接用对用 ApiAi A p i ∗ A i 直接更新长度在 [1,RminiLmini+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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值