[RMQ] Codeforces 1454F Array Partition

题目大意

给你一个长为 (n(3leq nleq 2 imes 10^5)) 的数组 ({a_n}),问是否能把它分成三段,第一段的最大值等于第二段的最小值等于第三段的最大值。若能,输出方案。

题解

预处理每一个 (a_i) 在它之前最后一次作为前缀的最大值的位置 (pre[a[i]]),以及每一个 (a_i) 在它之后最后一次作为后缀的最大值的位置 (suf[a[i]])。遍历每一个 (a[i]),若 ([pre[a[i]]+1,suf[a[i]-1]]) 的区间最小值等于 (a[i]),则数组可分为 ([1,pre[a[i]]],[pre[a[i]]+1,suf[a[i]]-1],[suf[a[i]],N]) 这三段。使用 ST 表维护即可,时间复杂度 (O(Nlog N))

Code

#include <bits/stdc++.h>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

template<typename elemType>
struct ST{
    elemType dp[200005][20];
    int N;
    ST(int _N=0):N(_N){}
    void Init(elemType *data,int Len){
        N=Len;
        for(RG i=1;i<=N;i++)
            dp[i][0]=data[i];
        for(RG j=1;(1<<j)<=N;j++)
            for(RG i=1;i+(1<<j)-1<=N;i++)
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        return;
    }
    elemType Query(int L,int R){
        int K=log2(R-L+1);
        return min(dp[L][K],dp[R-(1<<K)+1][K]);
    }
};
ST<int> RMQ;
map<int,int> pre,suf;
int a[200010],pos[200010];
int T,N;

void Solve(){
    pre.clear();suf.clear();
    int mx=-1;
    for(int i=N;i>=1;--i){
        pos[i]=suf[a[i]];
        mx=max(mx,a[i]);
        suf[mx]=i;
    }
    mx=-1;
    for(int i=1;i<=N;++i){
        if(pre[a[i]] && pos[i]>i){
            int L=pre[a[i]],R=pos[i];
            if(RMQ.Query(L+1,R-1)==a[i]){
                cout<<"YES"<<endl;
                cout<<L<<" "<<R-L-1<<" "<<N-R+1<<endl;
                return;
            }
        }
        mx=max(mx,a[i]);
        pre[mx]=i;
    }
    cout<<"NO"<<endl;
}

int main(){
    Read(T);
    while(T--){
        Read(N);
        for(int i=1;i<=N;++i)
            Read(a[i]);
        RMQ.Init(a,N);
        Solve();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/AEMShana/p/14040408.html