【題解】[Codeforces 1359D] Yet Another Yet Another Task【單調棧 ST 表】
阿新 • • 發佈:2020-11-02
題意
給定一序列,求其一個子段,滿足該子段的和減去其最大值的結果儘可能大。\(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; }