[RMQ] Codeforces 1454F Array Partition
题目大意
给你一个长为 \(n(3\leq n\leq 2\times 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(N\log 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;
}