单调栈+线段树

  • 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(1≤n≤5×105).

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

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

时间: 2024-07-30 22:38:55

单调栈+线段树的相关文章

【CF671E】Organizing a Race 单调栈+线段树

[CF671E]Organizing a Race 题意:n个城市排成一排,每个城市内都有一个加油站,赛车每次经过第i个城市时都会获得$g_i$升油.相邻两个城市之间由道路连接,第i个城市和第i+1个城市之间的道路长度为$w_i$,走一单位的路要花1升油.你想在某两个城市之间举办一场锦标赛.如果你选择的两个城市分别是a和b(a<b),则具体过程如下: 1. 赛车从a开始往右走一直走到b,走过城市时会在加油站加油,走过道路时会消耗油,且一开始时就已经在a处加完油了.你需要满足赛车能有足够的油能从a

2019南昌网络赛-I(单调栈+线段树)

题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上弄了3小时,思路大体都是对的,但就是没法实现.这几天恶补线段树. 首先可以利用单调栈来查找满足a[i]为最小值的最大区间L[i]~R[i].然后利用线段树求一个段的和sum.最小前缀lsum和最小后缀rsum.然后遍历a[i]: a[i]>0:最优为sum(L[i],R[i])*a[i] a[i]<

计蒜客38228 Max answer 单调栈 + 线段树

Max answer 与POJ 2796 Feel Good类似,但是这个题有负数,需要特殊处理一下 #include <bits/stdc++.h> #define DBG(x) cerr << #x << " = " << x << endl using namespace std; typedef long long LL; #define iall 1, n, 1 #define lrrt int l, int r,

BZOJ 1012: [JSOI2008]最大数maxnumber 单调队列/线段树/树状数组/乱搞

1012: [JSOI2008]最大数maxnumber Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 4750  Solved: 2145[Submit][Status][Discuss] Description 现 在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度. 2. 插入操作.语法:A n 功能:将n加上t,其中t是最近一

bzoj-2286 消耗战【虚树+倍增lca+单调栈】

2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 1815  Solved: 645 [Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是

【bzoj3956】Count 单调栈+可持久化线段树

题目描述 输入 输出 样例输入 3 2 0 2 1 2 1 1 1 3 样例输出 0 3 题解 单调栈+可持久化线段树 本题是 bzoj4826 的弱化版(我为什么做题总喜欢先挑难的做QAQ) $k$对点对$(i,j)$有贡献,当且仅当$a_k=max(a_{i+1},a_{i+2},...,a_{r-1})$,且$a_k<a_i\&\&a_k<a_j$. 那么我们可以使用单调栈求出i左面第一个比它大的位置$lp[i]$,和右面第一个比它大的位置$rp[i]$,那么点对$(lp

[JXOI2017]颜色 线段树扫描线 + 单调栈

---题面--- 题解: 首先题目要求删除一些颜色,换个说法就是要求保留一些颜色,那么观察到,如果我们设ll[i]和rr[i]分别表示颜色i出现的最左边的那个点和最右边的那个点,那么题目就是在要求我们选出的区间要满足区间[l, r]内所有颜色的max(rr[i]) <= r,并且min(ll[i]) >= l. 因为是区间相关的问题,又涉及到左右端点,因此我们考虑扫描线,那么考虑如何维护它. 因为每个颜色的ll[i]和rr[i]可以看做构成了一个区间,那么现在已经进入线段树的节点就分2种情况.

Educational Codeforces Round 61 (Rated for Div. 2) G(线段树,单调栈)

#include<bits/stdc++.h>using namespace std;int st[1000007];int top;int s[1000007],t[1000007];int mx[4000007];int sum[4000007];int head[1000007],to[2000007],nex[2000007];int n,k;int a[10000077];int dfn;int tot;void pushup(int rt){    mx[rt]=max(mx[rt

Codeforces 780G Andryusha and Nervous Barriers 线段树套set || 线段树套单调栈

Andryusha and Nervous Barriers 问题本质我们只需要找出每个线段两端下面第一个碰到的是哪个线段就好啦. 按照 h 排序我们就能用线段树套set 不管维护什么都能维护出这个东西,但是 如果set里维护 u + s的话,就能优化成单调栈, 好优秀啊. #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #define fi f