D. Yet Another Yet Another Task
题:https://codeforces.com/contest/1359/problem/D
题意:给定一个序列,你可以选择一个区间[L,R],你需要remove掉其中最大的,再sum{L,R},问最大可能是多少。
分析:也就是说,我们可以枚举每个数ai作为最大值,然后再ai左边找前缀和最小的,在ai后边找前缀和最大的,这个用线段树可以实现(简单嘛,也可以用其他数据结构),关键是要在哪些区间选取是最大的,要保证当前ai最大只需要预处理:ai 最左能达到哪个位置使经过的位置的值都比ai小,就可以确定线段树选取的区间了,向右也是一样,这一部分用单调区间的做法即可。
#include<bits/stdc++.h> using namespace std; #define pb push_back #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r typedef long long ll; const int inf=0x3f3f3f3f; const ll INF=1e18; const int M=1e5+5; struct node{ int mi,mx; }tr[M<<2]; int a[M],l[M],r[M],sum[M]; void up(int root){ tr[root].mi=min(tr[root<<1].mi,tr[root<<1|1].mi); tr[root].mx=max(tr[root<<1].mx,tr[root<<1|1].mx); } void build(int root,int l,int r){ if(l==r){ tr[root].mi=tr[root].mx=sum[l]; return ; } int midd=(l+r)>>1; build(lson); build(rson); up(root); } int querymi(int L,int R,int root,int l,int r){ if(L<=l&&r<=R){ return tr[root].mi; } int midd=(l+r)>>1; int res=inf; if(L<=midd) res=querymi(L,R,lson); if(R>midd) res=min(res,querymi(L,R,rson)); return res; } int querymx(int L,int R,int root,int l,int r){ if(L<=l&&r<=R){ return tr[root].mx; } int midd=(l+r)>>1; int res=-inf; if(L<=midd) res=querymx(L,R,lson); if(R>midd) res=max(res,querymx(L,R,rson)); return res; } int main(){ int n; scanf("%d",&n); a[1]=0,n++; for(int i=0;i<=4*n;i++) tr[i].mi=inf,tr[i].mx=-inf; for(int i=2;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i]; build(1,1,n); int ans=0; for(int i=2;i<=n;i++){ l[i]=i; while(a[l[i]-1]<=a[i]&&l[i]-1>=2){ l[i]=l[l[i]-1]; } } for(int i=n;i>=2;i--){ r[i]=i; while(a[r[i]+1]<=a[i]&&r[i]+1<=n){ r[i]=r[r[i]+1]; } } for(int i=2;i<=n;i++){ int t1=querymi(l[i]-1,i-1,1,1,n); int t2=querymx(i,r[i],1,1,n); ans=max(ans,t2-t1-a[i]); } printf("%d\n",ans); return 0; }