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;
}