单调栈+线段树

  •  262144K
 

Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.

Now she is planning to find the max value of the intervals in her array. Can you help her?

Input

First line contains an integer n(1 \le n \le 5 \times 10 ^5n(1n5×105).

Second line contains nn integers represent the array a (-10^5 \le a_i \le 10^5)a(105ai105).

Output

One line contains an integer represent the answer of the array.

样例输入复制
5
1 2 3 4 5
样例输出复制
36

陈你

题意 : 给你 n 个元素,有正有负,要求你去寻找一个区间,要求区间中最小的元素乘以此元素所在的一段区间的和要最大,输出最大值

思路分析:

  若元素全为正,显然就是单调栈的入门题了,但是现在元素有负的,因此需要换个角度去考虑

  借助单调栈,我们可以找到每个元素作为最小值所在的区间

  假设现在选取的一个元素是负的,那么我们是不就要找一段负的区间的和最小,乘起来才会使最大

  那么这个时候就可以借助前缀和,该位置右边的最小值减去左边的最大值即可,若左边最大值小于0,此时就不要减去

  用线段树去维护

  一直有个地方写错,就是单调栈找每个元素所在区间的地方

 

代码示例:

#define ll long long
const ll maxn = 5e5+5;
const ll mod = 1e9+7;
const double eps = 1e-9;
const double pi = acos(-1.0);
const ll inf = 0x3f3f3f3f;

ll n;
ll a[maxn];
ll lf[maxn], rf[maxn];
struct pp
{
    ll num, id;
};
struct pp sta[maxn];

struct node
{
    ll l, r;
    ll mm, mi;
}t[maxn<<2];
#define lson k<<1
#define rson k<<1|1
ll sum[maxn];
void init(){
    ll top = 0;
    
    a[n+1] = -999999999;
    for(ll i = 1; i <= n+1; i++){
        while(top > 0 && sta[top].num > a[i]){
            rf[sta[top].id] = i-1;
            top--; 
        }
        sta[++top] = {a[i], i};
    }
    top = 0; 
    for(ll i = 1; i <= n+1; i++){
        ll pos = i;
        while(top > 0 && sta[top].num > a[i]){
            pos = sta[top].id;
            top--;
        }
        sta[++top] = {a[i], pos};
        lf[i] = pos;
    } 
}

void build(ll l, ll r, ll k){
    t[k].l = l, t[k].r = r;
    if (l == r){
        t[k].mm = t[k].mi = sum[l];
        return;
    }
    ll mid = (l+r)>>1;
    build(l, mid, lson);
    build(mid+1, r, rson);
    t[k].mm = max(t[lson].mm, t[rson].mm);
    t[k].mi = min(t[lson].mi, t[rson].mi);
}

ll qmax(ll l, ll r, ll k){
    ll ans = -1e18;
    if (l <= t[k].l && t[k].r <= r) {
        return t[k].mm;
    }
    ll mid = (t[k].l+t[k].r)>>1;
    if (l <= mid) ans = max(ans, qmax(l, r, lson));
    if (r > mid) ans = max(ans, qmax(l, r, rson));
    return ans;
}

ll qmin(ll l, ll r, ll k){
    ll ans = 1e18;
    if (l <= t[k].l && t[k].r <= r) {
        return t[k].mi;
    }
    ll mid = (t[k].l+t[k].r)>>1;
    if (l <= mid) ans = min(ans, qmin(l, r, lson));
    if (r > mid) ans = min(ans, qmin(l, r, rson));
    return ans;
}


void solve(){
    for(ll i = 1; i <= n; i++){
        sum[i] = sum[i-1]+a[i];
    }
    ll ans = 0;
    build(1, n, 1);
    for(ll i = 1; i <= n; i++){
        if (a[i]>0) ans = max(ans, (sum[rf[i]]-sum[lf[i]-1])*a[i]);
        else {
            ans = max(ans, a[i]*(qmin(i, rf[i], 1ll)-max(qmax(lf[i], i, 1ll), 0ll)));
        }
    }
    printf("%lld\n", ans);
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    cin >> n;
    for(ll i = 1; i <= n; i++){
        scanf("%lld", &a[i]);
    }    
    init();
    solve();
    return 0;
}

  

转载于:https://www.cnblogs.com/ccut-ry/p/10803287.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值