質問:
次の2つの条件を満たす3つの正の整数x、y、zを見つけます。
アイデア:最初に、プレフィックスとサフィックスの最大値を簡単に処理できます。プレフィックスの最大値を直接トラバースできます。最大値接尾辞のは、接尾辞を維持するための配列sufとして開かれます。最大値で十分です。真ん中の部分は、変更操作がないため、静的な間隔の問題です。前処理用のstテーブルを作成するだけです。
次に、私たちの難しさは、x + yの位置を見つけて、方程式を満たす方法にあります。
まず第一に、私たちはこれらの2つの常識を知る必要があります。
間隔の最小値は、間隔
の長さが増加するにつれて同じまたは減少します。間隔の最大値は、間隔
の長さが増加するにつれて同じまたは増加します。次に、ここに単調性があります。
したがって、各位置をxとして列挙し、二分法によってx + yの位置を取得します。
1.接頭辞ma> min(x + 1、x + y)の最大値が間隔の最小値が小さすぎることを示している場合、それを増やしたい場合は、間隔の長さを短くする必要があります。左の終点は変更されていないので、r = mとし、右の終点を減らします。
2.接頭辞ma <min(x + 1、x + y)の最大値が、間隔の最小値が大きすぎてそれを減らすことができないことを示している場合は、間隔の長さを増やし、左端点を増やす必要があります。変更されないままなので、l = mとし、右端点を展開します。
3. ma = min(x + 1、x + y)が満たされる場合、maと接尾辞の最大値、つまりmax(x + y + 1、n)も比較する必要があります。最大値の場合接頭辞のが大きい、接尾辞が小さすぎる、次に、右端点nが変更されていないため、間隔の長さを拡張する必要があります。したがって、左端点x + y + 1を小さくする必要があります。つまり、r = mとします。それ以外の場合は、l = mとします。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+50;
int lg[N],st[N][20];
int suf[N],a[N];
int getMin(int l,int r){
int k=lg[r-l+1];
return min(st[l][k],st[r-(1<<k)+1][k]);
}
int main(){
for(int i=2;i<N;i++){
lg[i]=lg[i-1];
if(i%2==0) lg[i]=lg[i/2]+1;
}
int T;cin>>T;
while(T--){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
st[i][0]=a[i];
}
for(int j=1;j<=18;j++){
for(int i=1;i<=n;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
suf[n]=a[n];
for(int i=n-1;i;i--) suf[i]=max(suf[i+1],a[i]);
int k=0;
int ma=0;
for(int i=1;i<n;i++){
ma=max(ma,a[i]);
int l=i,r=n;
while(l+1<r){
int m=l+r>>1;
int mi=getMin(i+1,m);
if(ma>mi) r=m;
else if(ma<mi) l=m;
else {
if(ma>suf[m+1]) r=m;
else if(ma<suf[m+1]) l=m;
else {
k=m;
break;
}
}
}
if(k){
cout<<"YES\n";
cout<<i<<" "<<k-i<<" "<<n-k<<endl;
break;
}
}
if(!k) cout<<"NO\n";
}
return 0;
}