a[i]的数据范围瞬间让这题变成了水题 我们枚举区间最大值 (从0到30) 负数没必要 因为当区间长度为1时ans为0 是最小值 我们只保留小于等于这个枚举值的数 然后维护最小前缀和就行了 如果碰到大于枚举值的位置 我们把最小前缀和设为正无穷
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int ans,b[N],sum[N];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
sum[i]=sum[i-1]+b[i];
}
for(int i = 0; i <= 30; i++){
int mn=2e9;
for(int j = 1; j <= n; j++){
if(b[j]>i){
mn=2e9;
continue;
}
mn=min(sum[j-1],mn);
ans=max(ans,sum[j]-mn-i);
}
}
printf("%d\n",ans);
}
不过如果a[i]没有这么小的范围限制呢,我们显然不能再去枚举最大值了。我们只能遍历数组,以每个位置为最大值统计答案 。首先要用单调栈求出每个位置左右最靠近的比它大的位置 例如数组 9 3 4 6 7 那么对于4来说 就是 位置1和位置5 用单调队列正向反向分别遍历就行 我们记为lt[i]和rt[i] 那么每个位置i的答案显然就是 lt[i]~i的最大后缀和加上i~rt[i]的最大前缀和减去a[i] 这一部分我是用线段树做的 用线段树维护前缀和数组sum[]的最大最小值 显然我们要求出lt[i]~i的最小前缀和以及i~rt[i]的最大前缀和
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int a[N],sum[N],tp,sta[N],lt[N],rt[N];
struct seg{
int l,r;
int mx,mi;
}T[N<<2];
const int inf = 1e9;
#define mid (T[id].l+T[id].r>>1)
#define ls id<<1
#define rs ls|1
#define lson ls,l,mid
#define rson rs,mid+1,r
void pushup(int id){
T[id].mx=max(T[ls].mx,T[rs].mx);
T[id].mi=min(T[ls].mi,T[rs].mi);
}
void build(int id,int l,int r){
T[id].l=l,T[id].r=r;
if(l==r){
T[id].mx=T[id].mi=sum[l];
return;
}
int m = l+r>>1;
build(ls,l,m);build(rs,m+1,r);
pushup(id);
}
int query(int id,int L,int R,int op){
if(L<=T[id].l&&R>=T[id].r){
return op?T[id].mx:T[id].mi;
}
int ans;
ans=op?-inf:inf;
if(L<=mid) ans=(op?max(ans,query(ls,L,R,op)):min(ans,query(ls,L,R,op)));
if(R>mid) ans=(op?max(ans,query(rs,L,R,op)):min(ans,query(rs,L,R,op)));
return ans;
}
int main(){
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
build(1,0,n);
a[0]=a[n+1]=inf;
sta[++tp]=0;
for(int i = 1; i <= n; i++){
while(tp>=1&&a[sta[tp]]<=a[i]) tp--;
lt[i]=sta[tp];
sta[++tp]=i;
}
tp=0;
sta[++tp]=n+1;
for(int i = n; i >= 1; i--){
while(tp>=1&&a[sta[tp]]<=a[i]) tp--;
rt[i]=sta[tp];
sta[++tp]=i;
}
/* for(int i = 1; i <= n; i++){
printf("lt[%d]=%d rt[%d]=%d\n",i,lt[i],i,rt[i]);
}
*/
int ans = 0;
for(int i = 1; i <= n; i++){
//printf("Q1=%d Q2=%d sum[i]=%d ",query(1,lt[i],i-1,0),query(1,i,rt[i]-1,1),sum[i]);
ans=max(ans,-query(1,lt[i],i-1,0)+query(1,i,rt[i]-1,1)-a[i]);
//printf("ans=%d\n",ans);
}
printf("%d\n",ans);
return 0;
}
队友(智杰巨巨)给了求后缀和最大值和前缀和最大值的方法,那就是拿线段树维护。在求区间最大子串和的时候 如果用线段树的话 我们都会维护最左最大子串和,最右最大子串和,区间最大子串和。那就可以直接从最左最大子串和,最右最大子串和下手了。一开始不知道怎么写query函数,后来智杰巨巨教我了。现在还是不很懂线段树能维护什么样的区间属性,看到过的是,这些属性要具有区间可加性。这个怎么说呢,如果能正确写出pushup函数的,应该都可以维护。做的题多了,能维护的东西就那些,但是线段树还是有很多很多匪夷所思的操作。还是得多学习。
#include<stdio.h>
#include<stack>
#include<algorithm>
using namespace std;
#define maxn 100010
int a[maxn];
int sum[maxn];
int x[maxn],y[maxn];
stack<int>s;
struct node{
int l,r;
int lmax,rmax;
};
node tree[400010];
void build(int k,int l,int r){
tree[k].l=l;
tree[k].r=r;
if(l==r){
tree[k].lmax=tree[k].rmax=a[l];
return ;
}
int mid=(l+r)/2;
build(2*k,l,mid);
build(2*k+1,mid+1,r);
tree[k].lmax=max(tree[2*k].lmax,sum[tree[2*k].r]-sum[tree[2*k].l-1]+tree[2*k+1].lmax);
tree[k].rmax=max(tree[2*k+1].rmax,sum[tree[2*k+1].r]-sum[tree[2*k+1].l-1]+tree[2*k].rmax);
}
int query(int k,int l,int r,int flag){
if(tree[k].l==l&&tree[k].r==r){
if(flag==0)return tree[k].lmax;
else return tree[k].rmax;
}
int mid=(tree[k].l+tree[k].r)/2;
int ans;
if(mid<r&&mid>=l){
if(flag==0){
ans=max(query(2*k,l,mid,flag),sum[mid]-sum[l-1]+query(2*k+1,mid+1,r,flag));
}
else{
ans=max(query(2*k+1,mid+1,r,flag),sum[r]-sum[mid]+query(2*k,l,mid,flag));
}
}
else if(mid>=r){
ans=query(2*k,l,r,flag);
}
else{
ans=query(2*k+1,l,r,flag);
}
return ans;
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
x[1]=1;
s.push(1);
for(int i=2;i<=n;i++){
if(a[i]<=0)continue;
while(!s.empty()&&a[s.top()]<=a[i]){
s.pop();
}
if(s.empty()){
x[i]=1;
}
else{
x[i]=s.top()+1;
}
s.push(i);
}
while(!s.empty())s.pop();
y[n]=n;
s.push(n);
for(int i=n-1;i>0;i--){
if(a[i]<=0)continue;
while(!s.empty()&&a[s.top()]<=a[i]){
s.pop();
}
if(s.empty()){
y[i]=n;
}
else{
y[i]=s.top()-1;
}
s.push(i);
}
build(1,1,n);
int ans=0;
for(int i=1;i<=n;i++){
if(a[i]<=0)continue;
ans=max(query(1,x[i],i,1)+query(1,i,y[i],0)-2*a[i],ans);
}
printf("%d\n",ans);
}