题目
点这里看题目。
分析
不难看出,对 \(A\) 排序后,\(P_3,P_4,\dots,P_7\) 在序列上一定是连续的。因此实际上需要枚举的只有 \(P_1,P_2,P_3\) 三个数。
我们需要做下决定:设 \(q=P_4+P_5+P_6+P_7-P_3\),则有 \(P_1<P_2+P_3\) 和 \(P_2<q\) 两条限制。如果我们枚举 \(P_1\) 或者 \(P_3\),限制会集中在一侧,在一侧上要选出条件牵连的两个数来,难度颇大;而如果我们枚举 \(P_2\),这两条限制分居两侧,从每侧选出一个数,简单不少。因此,我们会选择枚举 \(P_2\)。
枚举好 \(P_2\) 后,我们需要求的是满足 \(P_2<q\) 的最大的 \(P_3\)。但注意到,如果有 \(P_3\le P_3'\),且 \(q<q'\),那么 \(P_3\) 一定是不优于 \(P_3'\) 的。因此我们可以扫描 \(P_2\),使用单调栈维护合法的 \(P_3\),查询二分。最大化 \(P_3\) 之后,\(P_1\) 也可以直接二分。
因此,我们得到了 \(O(n\log n)\) 的算法。
小结:
- 审慎地决定思考的方向,对于问题的分析不应停留于找出性质、获得解法 等等,分析也应该可以为你导出合理的思考方向。
- 注意 \(P_3\) 待选点隐藏的单调性,类似的有「CSP-S2019」划分一题。
代码
#include <bits/stdc++.h>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
typedef long long LL;
const int MAXN = 5e5 + 5;
template<typename _T>
void read( _T &x )/*{{{*/
{
x = 0; char s = getchar(); int f = 1;
while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
x *= f;
}/*}}}*/
template<typename _T>
void write( _T x )/*{{{*/
{
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) write( x / 10 );
putchar( x % 10 + '0' );
}/*}}}*/
int stk[MAXN], top;
LL P[MAXN], pre[MAXN], coe[MAXN];
int N;
int main()
{
read( N );
rep( i, 1, N ) read( P[i] );
std :: sort( P + 1, P + 1 + N );
rep( i, 1, N ) pre[i] = pre[i - 1] + P[i];
LL ans = -1;
rep( i, 5, N )
{
if( top && coe[stk[1]] > P[i] )
{
int l = 1, r = top, mid;
while( l < r )
{
mid = ( l + r + 1 ) >> 1;
if( coe[stk[mid]] > P[i] ) l = mid;
else r = mid - 1;
}
int p = std :: lower_bound( P + 1, P + 1 + N, P[stk[l]] + P[i] ) - P - 1;
if( i < p && p <= N ) ans = std :: max( ans, coe[stk[l]] + 2 * P[stk[l]] + P[i] + P[p] );
}
coe[i] = pre[i - 1] - pre[i - 5] - P[i];
while( top && coe[stk[top]] <= coe[i] ) top --;
stk[++ top] = i;
}
write( ans ), putchar( '\n' );
return 0;
}