题意:
给你一个整数列表 x1,x2,,… ,xn 和一个数字 k,它保证从1到 k 的每个 i 至少出现在列表中一次。
现在求一个字典序最小的子序列,子序列有1到k组成
题解:
单调栈求解
我们先预处理,求出每种数最后出现的位置,用数组suf存
比如:这10个数:54321411155
suf[1] = 8
suf[2] = 4
suf[3] = 3
suf[4] = 6
suf[5] = 10
我们设一个栈,每次读入新的数据x(第i位)时与栈顶元素top比较,如果栈顶元素大于x,且suf[top]>i,我们就将栈顶弹出,直到该条件不满足,或者栈为空,然后将数据x存入栈内
原因:
如果suf[top]>i,说明在当前x的后面还会有和栈一样大小的数,而且栈顶大于x,我们为了让字典序更小,就可以用组合x top代替 top x
就比如说:5 4 5,
我们栈顶为5(第一个元素),读入第二个元素4,4比5小,且4之后还有5(第三个元素),说明可以组成字典序更小的4 5,而不是5 4,所以将5(第一个元素)弹出,将4存入
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}const int maxn=2e5+9;
ll a[maxn];
int vis[maxn];
int st[maxn],suf[maxn];
ll n,m;
int main(){cin>>n>>m;for(int i=1;i<=n;i++) a[i]=read();int s = 0;for(int i=1;i<=n;i++) suf[a[i]] = i;for(int i=1;i<=n;i++){if(vis[a[i]]) continue;while(s && a[i]<=a[st[s]] && suf[a[st[s]]]>i) //如果栈顶元素大于当前元素,且栈顶元素后面还会出现 vis[a[st[s--]]] = 0;//出栈,并标记为空 st[++s] = i;vis[a[i]] = 1;}for(int i=1;i<=m;i++)printf("%lld ",a[st[i]]);printf("\n");return 0;
}