P6510 奶牛排队(单调栈)

传送门

题意:
P6510 奶牛排队(单调栈)_第1张图片

给定序列,需要求出一段最长的子区间[i,j]满足,a[i]是严格最小,a[j]是严格最大 ,且长度不能为1,输出该长度。

  • 我们用两个单调栈,minst维护位于a[i]左边且小于a[i]的元素下标,
    maxst维护位于a[i]左边且大于等于a[i]的元素下标

  • 枚举B所在的位置i,然后利用maxst栈得到当前B所在位置往左第一个大于等于B的位置,也就是maxst的栈顶 maxst[top2]于是[maxst[top2]+1,i-1]这段区间每个数都是小于B的。

  • 然后我们只需要找到位于maxst[top2]右边且最近且小于B的元素即可,这些信息都储存在minst

  • 由于单调栈中存的是下标,下标单调递增,对应的值也是单调的,所以可以在minst中二分找到这个位置。

#include
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e5+10;
const int mx = 40;
const int mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
int n,a[maxn];
int minst[maxn],top1=0;//位于a[i]前且小于a[i]的元素下标 ,栈顶就是离i最近的那个
int maxst[maxn],top2=0;//位于a[i]前且大于等于a[i]的元素下标,栈定就是离i最近的那个

//维护两个单调栈  枚举B 在B左边第一个大于等于B的位置后面 找到最近的小于B的位置
//单调栈存的下标都是严格单调的 可以二分
int main()
{
    scanf("%d",&n);
    int ans=0;
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    //枚举B的位置 i
    for(int i=1;i<=n;i++) 
    {
        while(top1 && a[minst[top1]] >= a[i]) --top1;
        while(top2 && a[maxst[top2]] < a[i]) --top2;
        //minst[top1]就是a[i]左边第一个小于a[i]的元素下标
        //maxst[top2]就是a[i]左边第一个大于等于a[i]的元素下标
        int k=upper_bound(minst+1,minst+1+top1,maxst[top2])-minst;
        if(k != top1+1) ans=max(ans,i-minst[k]+1);
        minst[++top1]=i;
        maxst[++top2]=i;
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(双指针,数据结构)