[LOJ3153] 三级跳(单调栈 + 线段树)

problem

loj3153

solution

有一个显然正确但又不起眼却是正解必备的结论:

考虑 ( x , y , z ) (x,y,z) (x,y,z) 答案三元对,如果有一个数 i ∈ ( x , y ) ∧ a i ≥ a x i\in(x,y)\wedge a_i\ge a_x i(x,y)aiax,那么 ( i , y , z ) (i,y,z) (i,y,z) 一定是不劣于 ( x , y , z ) (x,y,z) (x,y,z) 的。

于是乎,我们就想到了用单调栈找出这样的 ( i , y ) (i,y) (i,y) 点对。

  • 具体而言:

    g i : g_i: gi: 存储所有 ( i , y ) (i,y) (i,y) y y y

    维护一个递减的单调栈,从 1 1 1 n n n 开始枚举,如果枚举到的 i i i a i > a s . t o p ( ) a_i>a_{s.top()} ai>as.top() 就弹栈,且把 i i i 放入 g s . t o p ( ) g_{s.top()} gs.top(),弹到 a i ≤ a s . t o p ( ) a_i\le a_{s.top()} aias.top() 为止。

    如果栈顶还有元素,就再把 i i i 放入 g s . t o p ( ) g_{s.top()} gs.top()

    最后把 i i i 入栈。

    这样所有 ( i , y ) (i,y) (i,y) 点对都挂在了 i i i 的下面。

处理出所有的点对后,我们把所有询问 [ l , r ] [l,r] [l,r] 挂在 l l l 下面。

从大到小枚举 i i i

先把 g i : ( i , y ) g_i:(i,y) gi:(i,y) 所有点对信息更改,当 z ≥ y ∗ 2 − i z\ge y*2-i zy2i 时, ( i , y , z ) (i,y,z) (i,y,z) 就是一个合法三元对。

这个时候的 z z z 对应着一段区间,我们容易想到线段树修改。

维护一棵线段树,树上每个节点表示做 z z z 时的最大 a x + a y + a z a_x+a_y+a_z ax+ay+az

a i + a y a_i+a_y ai+ay 修改 [ y ∗ 2 − i , n ] [y*2-i,n] [y2i,n] 区间,初始建树时就已经把 a z a_z az 挂在 z z z 节点上面了。

然后查询 l = i l=i l=i 的所有询问,在线段树上查询 [ l , r ] [l,r] [l,r] 区间。

这也是为什么要从大到小枚举的原因。

线段树具体维护可见代码。

code

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 500005
int n, Q;
int ans[maxn], a[maxn];
stack < int > s;
vector < int > g[maxn];
vector < pair < int, int > > q[maxn];

namespace SGT {
    struct node { int sum, tag, Max; }t[maxn << 2];
    #define lson now << 1
    #define rson now << 1 | 1
    #define mid  (l + r >> 1)
    void pushdown( int now ) {
        if( ! t[now].tag ) return;
        t[lson].tag = max( t[lson].tag, t[now].tag );
        t[lson].Max = max( t[lson].Max, t[lson].sum + t[now].tag );
        t[rson].tag = max( t[rson].tag, t[now].tag );
        t[rson].Max = max( t[rson].Max, t[rson].sum + t[now].tag );
        t[now].tag = 0;
    }
    void build( int now, int l, int r ) {
        if( l == r ) { t[now].sum = a[l]; return; }
        build( lson, l, mid );
        build( rson, mid + 1, r );
        t[now].sum = max( t[lson].sum, t[rson].sum );
    }
    void modify( int now, int l, int r, int L, int R, int val ) {
        if( R < l or r < L or L > R ) return;
        if( L <= l and r <= R ) {
            t[now].tag = max( t[now].tag, val );
            t[now].Max = max( t[now].Max, t[now].sum + val );
            return;
        }
        pushdown( now );
        modify( lson, l, mid, L, R, val );
        modify( rson, mid + 1, r, L, R, val );
        t[now].Max = max( t[lson].Max, t[rson].Max );
    }
    int query( int now, int l, int r, int L, int R ) {
        if( R < l or r < L ) return -1e9;
        if( L <= l and r <= R ) return t[now].Max;
        pushdown( now );
        return max( query( lson, l, mid, L, R ), query( rson, mid + 1, r, L, R ) );
    }
}

signed main() {
    scanf( "%lld", &n );
    for( int i = 1;i <= n;i ++ ) {
        scanf( "%lld", &a[i] );
        while( ! s.empty() and a[s.top()] < a[i] ) 
            g[s.top()].push_back( i ), s.pop();
        if( ! s.empty() ) 
            g[s.top()].push_back( i );
        s.push( i );
    }
    SGT :: build( 1, 1, n );
    scanf( "%lld", &Q );
    for( int i = 1, l, r;i <= Q;i ++ ) {
        scanf( "%lld %lld", &l, &r );
        q[l].push_back( make_pair( r, i ) );
    }
    for( int i = n;i;i -- ) {
        for( int j : g[i] )
            SGT :: modify( 1, 1, n, j * 2 - i, n, a[i] + a[j] );
        for( auto it : q[i] )
            ans[it.second] = SGT :: query( 1, 1, n, i, it.first );
    }
    for( int i = 1;i <= Q;i ++ )
        printf( "%lld\n", ans[i] );
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值