• 单调队列与单调栈(入门)


    这些知识总算啃掉了...

    重要的思路就是加入栈和队列时还要加入元素的坐标位置。

    使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。

    单调栈就简单点,只有栈顶可以操作,若要找到从左到右第一个比i大的数,就保证严格递减,这样如果到第一个比i大的数是i就会被弹出,总之就是找什么条件的数,第i个数被弹出栈的条件就是什么,建议手打栈,代码如下:

    n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1;i<=n;i++)
        {
            while(top>=1&&a[i]>=q[top].s) ans=ans+i-q[top--].id-1;
            q[++top].id=i;q[top].s=a[i]; 
        }
        for(int i=top;i>=1;i--) ans=ans+n-q[top--].id;

    单调队列除了tail需要更新,还需要更新head,也就是及时把不符合信息的head出队,、

    单调队列性质:

    • 1、维护区间最值
    • 2、去除冗杂状态 如上题,区间中的两个元素a[i],a[j](假设现在再求最大值)
      若 j>i且a[j]>=a[i] ,a[j]比a[i]还大而且还在后面(目前a[j]留在队列肯定比a[i]有用,因为你是往后推, 核心思想 !
    • 其中第二条对动态规划仍使用...

    与题目联系时,记得把要求的元素放在队头就行了.

    例:

    for(int i=1;i<=k;i++)
        {
            while(head<=tail&&q[tail].s>=a[i]) tail--;
            q[++tail].id=i,q[tail].s=a[i];
        }
        mn[++o]=q[head].s;
        for(int i=k+1;i<=n;i++)
        {
            while(head<=tail&&q[tail].s>=a[i]) tail--;
            q[++tail].id=i,q[tail].s=a[i];
            while(q[head].id<=i-k) head++;
            mn[++o]=q[head].s;
        }

    好,让我们来看这个题:

    题目很简单,求连续的长度范围在p到q的最大长度和...

    暴力吗?O(n2)...

    我们暴力一定会用到前缀和,那我们就要来考虑能不能用前缀和保持单调性做...

    #include<bits/stdc++.h>
    #define ll long long
    #define max(a,b) (((a)>(b))?(a):(b))
    #define min(a,b) (((a)<(b))?(a):(b))
    using namespace std;
    const ll N=101000;
    ll ans=-1e18,sum[N],n,p,l,head,tail;
    struct gg
    {
        ll id,s;
    }q[N];
    inline ll read()
    {
        ll x=0,ff=1;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') ff=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*ff;
    } 
    int main()
    {
        freopen("1.in","r",stdin);
        cin>>n>>p>>l;
        for(register int i=1;i<=n;++i) sum[i]=sum[i-1]+read();
        for(register int i=p+1;i<=n;i++) 
        {
            while(head<=tail&&sum[i-p]<=q[tail].s) --tail;
            q[++tail].id=i-p;q[tail].s=sum[i-p];
            while(head<=tail&&q[head].id<i-l) ++head;
            ans=max(ans,sum[i]-q[head].s);  
        }
        cout<<ans<<endl;
        return 0;
    }

    这个题我们要记住的是范围,也就是说,你每次i向前推进时,你应该把哪些点放到队列里,这里就是把i-p放进去,因为有最小长度的限制,所以

    每次枚举i时,我们队列里控制的点范围其实在(i-q,i-p)之间,这样才能保证子序列时合法的子序列...

  • 相关阅读:
    hdu 5446 Unknown Treasure lucas和CRT
    Hdu 5444 Elven Postman dfs
    hdu 5443 The Water Problem 线段树
    hdu 5442 Favorite Donut 后缀数组
    hdu 5441 Travel 离线带权并查集
    hdu 5438 Ponds 拓扑排序
    hdu 5437 Alisha’s Party 优先队列
    HDU 5433 Xiao Ming climbing dp
    hdu 5432 Pyramid Split 二分
    Codeforces Round #319 (Div. 1) B. Invariance of Tree 构造
  • 原文地址:https://www.cnblogs.com/gcfer/p/10598916.html
Copyright © 2020-2023  润新知