2/9 康托展开+单调队列+单调栈(模拟栈)

Posted 钟钟终

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2/9 康托展开+单调队列+单调栈(模拟栈)相关的知识,希望对你有一定的参考价值。

首先贴一个重点,单调栈的经典问题
https://www.luogu.com.cn/problem/P7399
不难发现,每相邻的两个元素如果不相同,则它们所处的区间也不相同。
如果比前一个大,就说明又新增了一个区间,存至栈中,如果比它小,就说明前面又若干个区间结束了,并往前扫描,依次出栈,直到扫到比它大的或相等的,若是相等,说明处于同一区间,直接 continue 。

特殊情况:当 a[i]=0时,栈内的数会全部出栈,ans 会加个 1,但是当 a[i]=0a[i]=0 时,是不需要操作的,所以这种情况我们特殊判断。

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int maxn=3e6+5;
int n,a[maxn],f[maxn],ans;
stack<int>s;

signed main()

    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
    
        while(!s.empty()&&s.top()>a[i])
            s.pop();
        if(!s.empty()&&s.top()==a[i])
            continue;
        if(a[i])
            ans++,s.push(a[i]);
    
    cout<<ans<<endl;
    return 0;


https://www.luogu.com.cn/problem/P1901

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int maxn=3e6+5;
int n,a[maxn],vi[maxn],f[maxn],p[maxn],q[maxn],r;

signed main()

    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i],&vi[i]);
    for(int i=n;i>=1;i--)
    
        while(a[i]>=a[q[r]]&&r>0)
        
            f[i]+=vi[q[r]]; //可理解为从左往右的能量
    //若是比栈顶元素高,那么一定会收到栈内部分元素的能量值
            r--;
        
        if(r>0) f[q[r]]+=vi[i];  //可理解为从右往左的能量
    //若并没有栈顶元素高,那么栈顶元素一定会得到它的能量值
        q[++r]=i;//位于栈顶元素

    
    for(int i=2;i<=n;i++)
        f[i]=max(f[i],f[i-1]);
    cout<<f[n]<<endl;
    return 0;

https://www.luogu.com.cn/problem/P5367
全排列(康托展开是求全排列序号的方式)+树状数组
1.将数组都初始化为1,利用树状数组的方法快速得到前面有多少个数字比他小且还没被排(不包含自身,所以要减1)

留一下大佬的博客复习用(重点开公式和分析过程)
https://www.luogu.com.cn/blog/ravenclawyangrunze/solution-p5367

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=1e6+5;
int a[maxn],c[maxn],n,jc[maxn]=1,1,1,ans;

void update(int i,int val)

    while(i<=n)
    
        c[i]+=val;i+=i&-i;
    

int query(int i)

    int ret=0;
    while(i>0)
    
        ret+=c[i];i-=i&-i;
    
    return ret;

signed main()

    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    
        jc[i]=(jc[i-1]*i)%mod;
        update(i,1);
    
    for(int i=1;i<=n;i++)
    
        scanf("%lld",&a[i]);
        ans=(ans+((query(a[i])-1)*jc[n-i])%mod)%mod;
        update(a[i],-1);
    
    printf("%lld\\n",ans+1);
    return 0;

2.单调队列(看代码理解复习)
https://www.luogu.com.cn/problem/P4970

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int mod=998244353;
const int maxn=1e6+5;
int head,tail,n,k,a[maxn],p[maxn],q[maxn];
void queue_min()

    head=1;tail=0;    //保证模拟的队列中有元素
    for(int i=1;i<=n;i++)
    
        while(head<=tail&&p[tail]>a[i])  //队中有元素且尾部元素大于该元素
            tail--;                      //说明要换元素了
        p[++tail]=a[i];					//入队
        q[tail]=i;
        while(q[head]<=i-k)				//若头部元素小于框的左边界,则需要前进一格
            head++;
        if(i>=k)						//当大于等于框后,每次都要输出一个值
            cout<<p[head]<<" ";
    
    cout<<endl;

void queue_max()

    memset(p,0,sizeof(p));
    memset(q,0,sizeof(q));
    head=1;tail=0;
    for(int i=1;i<=n;i++)
    
        while(head<=tail&&p[tail]<a[i])
            tail--;
        p[++tail]=a[i];
        q[tail]=i;
        while(q[head]<=i-k)
            head++;
        if(i>=k)
            cout<<p[head]<<" ";
    
    cout<<endl;

signed main()

    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++)
    
        scanf("%lld",&a[i]);
    
    queue_min();
    queue_max();
    return 0;

3.单调栈https://www.luogu.com.cn/problem/P5788
数组模拟栈的实现:

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int maxn=3e6+5;
int n,a[maxn],f[maxn],p[maxn],q[maxn],r;

signed main()

    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=n;i>=1;i--)
    
        while(a[i]>=a[q[r]]&&r>0)
            r--;
        f[i]=q[r];
        q[++r]=i;//位于栈顶元素
    
    for(int i=1;i<=n;i++)
        printf("%d ",f[i]);
    cout<<endl;
    return 0;

使用栈stack

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int maxn=3e6+5;
int n,a[maxn],f[maxn];
stack<int>s;

signed main()

    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=n;i>=1;i--)
    
        while(!s.empty()&&a[s.top()]<=a[i])
            s.pop();
        if(s.empty()) //不为空返回0,为空返回1
            f[i]=0;
        else
            f[i]=s.top();
        s.push(i);
    
    for(int i=1;i<=n;i++)
        printf("%lld ",f[i]);
    cout<<endl;
    return 0;


以上是关于2/9 康托展开+单调队列+单调栈(模拟栈)的主要内容,如果未能解决你的问题,请参考以下文章

用数组模拟栈 队列 以及单调栈 单调队列应用

单调栈&单调队列入门

单调队列与单调栈用法详解

单调队列单调栈优先队列模板

单调栈以及单调队列

单调队列单调栈