[LOJ3153] 三级跳(单调栈 + 线段树)
problem
loj3153
solution
有一個顯然正確但又不起眼卻是正解必備的結論:
考慮 (x,y,z)(x,y,z)(x,y,z) 答案三元對,如果有一個數 i∈(x,y)∧ai≥axi\in(x,y)\wedge a_i\ge a_xi∈(x,y)∧ai?≥ax?,那么 (i,y,z)(i,y,z)(i,y,z) 一定是不劣于 (x,y,z)(x,y,z)(x,y,z) 的。
于是乎,我們就想到了用單調棧找出這樣的 (i,y)(i,y)(i,y) 點對。
-
具體而言:
記 gi:g_i:gi?: 存儲所有 (i,y)(i,y)(i,y) 的 yyy。
維護一個遞減的單調棧,從 111 到 nnn 開始枚舉,如果枚舉到的 iii 位 ai>as.top()a_i>a_{s.top()}ai?>as.top()? 就彈棧,且把 iii 放入 gs.top()g_{s.top()}gs.top()?,彈到 ai≤as.top()a_i\le a_{s.top()}ai?≤as.top()? 為止。
如果棧頂還有元素,就再把 iii 放入 gs.top()g_{s.top()}gs.top()?。
最后把 iii 入棧。
這樣所有 (i,y)(i,y)(i,y) 點對都掛在了 iii 的下面。
處理出所有的點對后,我們把所有詢問 [l,r][l,r][l,r] 掛在 lll 下面。
從大到小枚舉 iii。
先把 gi:(i,y)g_i:(i,y)gi?:(i,y) 所有點對信息更改,當 z≥y?2?iz\ge y*2-iz≥y?2?i 時,(i,y,z)(i,y,z)(i,y,z) 就是一個合法三元對。
這個時候的 zzz 對應著一段區間,我們容易想到線段樹修改。
維護一棵線段樹,樹上每個節點表示做 zzz 時的最大 ax+ay+aza_x+a_y+a_zax?+ay?+az?。
用 ai+aya_i+a_yai?+ay? 修改 [y?2?i,n][y*2-i,n][y?2?i,n] 區間,初始建樹時就已經把 aza_zaz? 掛在 zzz 節點上面了。
然后查詢 l=il=il=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; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的[LOJ3153] 三级跳(单调栈 + 线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 灰色卫衣衣服颜色搭配技巧 灰色卫衣衣服怎
- 下一篇: 百天给宝宝拍照技巧 怎么给宝宝照百天照