【题解】[Codeforces 1359D] Yet Another Yet Another Task【单调栈 ST 表】

题目链接

题意

给定一序列,求其一个子段,满足该子段的和减去其最大值的结果尽可能大。\(n\leq 10^5\)

题解

考虑每个数作为最大值时的最大答案。每个数作为最大值时区间左右端点的范围可以单调栈预处理。于是每次便要查询一段内的最小前缀和和一段内的最大前缀和。ST 表可做(感觉也可以在单调栈的时候一起处理?)。

代码:

#include<bits/stdc++.h>
using namespace std;
int getint(){
	int ans=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		ans=ans*10+c-'0';
		c=getchar();
	}
	return ans*f;
}
const int N=1e5+10,L=18;
int a[N],l[N],r[N],s[N],n;
int smin[L][N],smax[L][N],l2[N];
void initst(){
    for(int i=0;i<=n;i++)smin[0][i]=smax[0][i]=s[i];
    for(int i=1;i<L;i++){
        for(int j=0;j<=n-(1<<i)+1;j++){
            smin[i][j]=min(smin[i-1][j],smin[i-1][j+(1<<i-1)]);
            smax[i][j]=max(smax[i-1][j],smax[i-1][j+(1<<i-1)]);
        }
    }
}
int qmin(int l,int r){
    ++r;
    int t=l2[r-l];
    return min(smin[t][l],smin[t][r-(1<<t)]);
}
int qmax(int l,int r){
    ++r;
    int t=l2[r-l];
    return max(smax[t][l],smax[t][r-(1<<t)]);
}
int main(){
    n=getint();
    for(int i=1;i<=n;i++)a[i]=getint(),s[i]=s[i-1]+a[i];
    for(int i=2;i<=n;i++)l2[i]=l2[i>>1]+1;
    initst();

    a[0]=a[n+1]=0x7f7f7f7f;
    stack<int>stk;
    stk.push(0);
    for(int i=1;i<=n;i++){
        while(a[stk.top()]<=a[i])stk.pop();
        l[i]=stk.top();
        stk.push(i);
    }
    while(stk.size())stk.pop();
    stk.push(n+1);
    for(int i=n;i>=1;--i){
        while(a[stk.top()]<=a[i])stk.pop();
        r[i]=stk.top()-1;
        stk.push(i);
    }
    
    int ans=0;
    for(int i=1;i<=n;i++){
        int s1=qmin(l[i],i-1),s2=qmax(i,r[i]);
        ans=max(ans,s[i-1]-s1 + s2-s[i]);
    }
    
    cout<<ans;
}

posted @ 2020-11-02 10:37  破壁人五号  阅读(73)  评论(0编辑  收藏  举报