扫描线大佬真的强……
我来说说我的奇怪做法。
根据题意,只要先手选定一个区间之后,拿掉的一定是最大值,那么我们可以考虑枚举这个被拿掉的最大值。
于是考虑每个数作为最大值能影响的范围,显然可以用单调栈求出左右第一个比它大的。
记 i i i在 [ l , r ] [l,r] [l,r]内是最大值,那么我们就是要从这个位置开始,分别向左向右找到一段和最大的子段。
先考虑右边,左边同理。我们对原序列进行前缀和,那么也就是要找到 [ i , r ] [i,r] [i,r]中前缀和的最大值。所以我们用st表预处理,查询区间最大值即可。
代码中为了方便,开了正反两个数组,分别处理左右情况。
#include <bits/stdc++.h>
#define MAX 100005
using namespace std;
int n;
int a[MAX], lg[MAX];
struct solve{
int s[MAX], st[MAX][22], r[MAX], q[MAX];
int query(int l, int r){
int len = lg[r-l+1];
return max(st[l][len], st[r-(1<<len)+1][len]);
}
void build(int *a){
memset(s, 0, sizeof(s));
for(int i = 1; i <= n; i++){
s[i] = st[i][0] = s[i-1]+a[i];
}
for(int j = 1; j <= 19; j++){
for(int i = 1; i+(1<<j-1) <= n; i++){
st[i][j] = max(st[i][j-1], st[i+(1<<j-1)][j-1]);
}
}
int top = 0;
for(int i = 1; i <= n; i++){
while(top && a[q[top]] < a[i]) r[q[top]] = i, top--;
q[++top] = i;
}
for(int i = 1; i <= top; i++){
r[q[i]] = n+1;
}
}
}s1, s2;
int main()
{
cin >> n;
lg[0] = -1;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
lg[i] = lg[i/2]+1;
}
s1.build(a);
reverse(a+1, a+n+1);
s2.build(a);
int ans = (int)-1e9;
for(int i = 1; i <= n; i++){
int p1 = i, p2 = n-p1+1;
int t1 = s1.query(p1, s1.r[p1]-1)-s1.s[p1], t2 = s2.query(p2, s2.r[p2]-1)-s2.s[p2];
ans = max(ans, t1+t2);
}
cout << ans << endl;
return 0;
}