题目:click
题意:找到一段区间的总和减该区间的最大值的最大值。
一开始想的是dp找最大的连续子序列的MAX减该段的MAX,仔细一想可以发现问题,这个思路是不对的,我们考虑先去把a[i]看做是该区间的最大值,进行枚举处理,单调队列确定该区间的端点,SUM数组去做前缀和。直接减吗?进一步思考可以发现是错误的。。。可能边缘全是负数我们并不需要,直接用ST表去维护一个区间的max和min,找到[i,r]区间和最大的,减去[l,i-1]区间和最小的得最大值。注意一下细节,st表从0开始。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
int n;
int a[100100];
int LMAX[100100],RMAX[100100];
int que[100100];
int SUM[100100];
int stmax[100100][20];
int stmin[100100][20];
void init(int n)
{
int i,j,k;
for(i=0;i<=n;i++)
stmax[i][0]=stmin[i][0]=SUM[i];
int t=log2(n)+1;
for(int j=1;j<t;j++)
{
for(int i=0;i<=n-(1<<j)+1;i++)
{
stmax[i][j]=max(stmax[i][j-1],stmax[i+(1<<(j-1))][j-1]);
stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<(j-1))][j-1]);
}
}
}
int solvemax(int l,int r)
{
int k=log2(r-l+1);
return max(stmax[l][k],stmax[r-(1<<k)+1][k]);
}
int solvemin(int l,int r)
{
int k=log2(r-l+1);
return min(stmin[l][k],stmin[r-(1<<k)+1][k]);
}
int main()
{
scanf("%d",&n);
int i,j,k;
SUM[0]=0;
int ans=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
SUM[i]=SUM[i-1]+a[i];
}
init(n);
int h=1,t=0;
for(i=1;i<=n;i++)
{
while(h<=t&&a[que[t]]<=a[i])
{
t--;
}
if(h<=t)
LMAX[i]=que[t];
else
LMAX[i]=0;
que[++t]=i;
}
h=1,t=0;
for(i=n;i>=1;i--)
{
while(h<=t&&a[que[t]]<=a[i])
{
t--;
}
if(h<=t)
RMAX[i]=que[t]-1;
else
RMAX[i]=n;
que[++t]=i;
}
for(i=1;i<=n;i++)
{
int l=LMAX[i];
int r=RMAX[i];
ans=max(ans,solvemax(i,r)-solvemin(l,i-1)-a[i]);
}
printf("%d",ans);
return 0;
}