【题解】 CF1359D 【Yet Another Yet Another Task】

扫描线大佬真的强……

我来说说我的奇怪做法。

根据题意,只要先手选定一个区间之后,拿掉的一定是最大值,那么我们可以考虑枚举这个被拿掉的最大值。

于是考虑每个数作为最大值能影响的范围,显然可以用单调栈求出左右第一个比它大的。

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

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/109486369