题目链接:POJ-2452
主要思路:
对于每一个,从它后面找第一个比它小的数(若没有则为结尾+1),即为R,在
这段区间内找到最大值,其位置即为j,左端点为i的最长合法区间即为
.对于每个i,若它的R=i则跳过.
PS:若这段区间的最大值有多个,则必须取前面那个,否则会取出非法区间,如:
8
3 11 6 8 9 10 11 1
其答案应为4而不是6.
找i后面第一个比它小的数可以用单调栈来完成,找区间内最大值与其位置可以用线段树来完成.(在线段树内存最大值也要若有多个也得存前面那个的位置).
AC代码:
#include<cstdio>
#define M 50005
struct node {
int L,R,mx,pos;//mx为最大值的值,pos为最大值的位置.
void Init() {
mx=0,pos=0;
}
};
int max(int x,int y) {
return x>y?x:y;
}
struct Segment {
node tree[M<<2];
int stk[M],R[M];
int A[M];
void Up(int p) {
if(tree[p<<1].mx<tree[p<<1|1].mx){
tree[p].pos=tree[p<<1|1].pos;
tree[p].mx=tree[p<<1|1].mx;
}else{//若前面的最大值等于后面的最大值也去前面的最大值的位置
tree[p].pos=tree[p<<1].pos;
tree[p].mx=tree[p<<1].mx;
}
}
void Build(int L,int R,int p) {
tree[p].Init();
tree[p].L=L,tree[p].R=R;
if(L==R) {
tree[p].pos=L;
tree[p].mx=A[L];
return;
}
int mid=L+R>>1;
Build(L,mid,p<<1);
Build(mid+1,R,p<<1|1);
Up(p);//从子状态里收集状态
}
int find(int L,int R,int p){
if(L==tree[p].L&&tree[p].R==R){
return tree[p].pos;
}
int mid=tree[p].L+tree[p].R>>1;
if(R<=mid)return find(L,R,p<<1);
else if(L>mid)return find(L,R,p<<1|1);
else{
int F1=find(L,mid,p<<1);
int F2=find(mid+1,R,p<<1|1);
if(A[F1]!=A[F2])return A[F1]>A[F2]?F1:F2;//若前后区间最大值不同就去最大值大的那个区间
return F1;//若相同则取前面那个区间的位置
}
}
void Run(int n) {
int top=0,ans=0;
stk[0]=n+1;
A[n+1]=-2e9;//监视哨
for(int i=n; i>=1; i--) {
while(A[stk[top]]>A[i])top--;//找这个元素后面第一个小于这个元素的位置
R[i]=stk[top]-1;//注意是减1
stk[++top]=i;
}
for(int i=1; i<=n; i++){
if(i!=R[i]){//若该元素后面第一个元素就比它小
int pos=find(i,R[i],1);
ans=max(ans,pos-i);
}
}
printf("%d\n",ans==0?-1:ans);
}
} S;
int main() {
int n;
while(~scanf("%d",&n)) {
for(int i=1; i<=n; i++)scanf("%d",&S.A[i]);
S.Build(1,n,1);//建树
S.Run(n);
}
}