CF1359D Yet Another Yet Another Task(思维)

这题题意很清晰,就是求取区间和-区间最大值的最大

如果我们考虑直接求区间和再减最大值,显然复杂度比较高

因此考虑枚举每个位置,然后找到以他为最大值的最大区间

那么答案就是这段区间l-r中,i-r中的前缀和最大值减去l-1-i中前缀和最小值,这就是这段区间的最大和,之后减去当前位置即可

这种最大最小前缀和可以直接用st表维护表示区间的最大前缀和。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10;
int lg[N];
int ma[N][21];
int mi[N][21];
int sum[N];
vector<int> num[110];
int a[N];
void init(){
    int i;
    lg[1]=0;
    lg[2]=1;
    for(i=3;i<N;i++){
        lg[i]=lg[i>>1]+1;
    }
}
int find(int opt,int l,int r){
    if(opt==1){
        int p=lg[r-l+1];
        return min(mi[l][p],mi[r-(1<<p)+1][p]);
    }
    else{
        int p=lg[r-l+1];
        return max(ma[l][p],ma[r-(1<<p)+1][p]);
    }
}
int main(){
    init();
    int n;
    cin>>n;
    int i;
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
        num[a[i]+30].push_back(i);
    }
    int j;
    for(j=0;j<21;j++){
        for(i=0;i+(1<<j)-1<=n;i++){
            if(j==0){
                ma[i][j]=sum[i];
                mi[i][j]=sum[i];
            }
            else{
                ma[i][j]=max(ma[i][j-1],ma[i+(1<<(j-1))][j-1]);
                mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int res=-0x3f3f3f3f;
    for(i=1;i<=n;i++){
        int l=1,r=n;
        for(j=a[i]+1;j<=30;j++){
            int pos=lower_bound(num[j+30].begin(),num[j+30].end(),i)-num[j+30].begin();
            if(pos<(int)num[j+30].size())
            r=min(r,num[j+30][pos]-1);
            if(pos>=1)
            l=max(l,num[j+30][pos-1]+1);

        }
        int pre=find(1,l-1,i);
        int nxt=find(2,i,r);
        if(nxt!=pre)
            res=max(res,nxt-pre-a[i]);
    }
    if(res==-0x3f3f3f3f)
        cout<<0<<endl;
    else
        cout<<res<<endl;
}
View Code

 

posted @ 2020-06-30 23:28  朝暮不思  阅读(199)  评论(0编辑  收藏  举报