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 康托展开+单调队列+单调栈(模拟栈)的主要内容,如果未能解决你的问题,请参考以下文章