POJ-2452 Sticks Problem 单调栈+线段树

题目链接:POJ-2452

主要思路:

对于每一个A[i],从它后面找第一个比它小的数(若没有则为结尾+1),即为R,在[i,R)这段区间内找到最大值,其位置即为j,左端点为i的最长合法区间即为[i,j].对于每个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);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81142202