单调栈解法
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
const int maxn=3e5+4;
int pos1[maxn],pos2[maxn],d[maxn]; //pos1[i]表示以i位置结尾的LIS长度,d[i]表示长度为i的LIS结尾元素最小值
int ans[maxn],a[maxn],p[maxn];
int n,tot;
int main(){
while(~scanf("%d",&n)){
memset(d,0x3f,sizeof(d));
d[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",a+i);
pos1[i]=lower_bound(d+1,d+1+n,a[i])-d;
d[pos1[i]]=a[i];
}
memset(d,0x3f,sizeof(d));
d[0]=0;
for(int i=n;i>0;--i){
pos2[i]=lower_bound(d+1,d+n+1,a[i])-d;
d[pos2[i]]=a[i];
}
//计算字典序最小的情况
//从左至右遍历找到的是第一个最长串的峰
//从这个峰向两侧找组成LIS的顶点即可,向左需要用单调栈尽可能向前找(遇到同样满足条件的,栈会弹出抛弃原有的),向右只需贪心遍历(有满足条件的就要了)即可
int mx=pos1[1]+pos2[1];
int now=1;
for(int i=2;i<=n;i++){
if(pos1[i]+pos2[i]>mx){
mx=pos1[i]+pos2[i];
now=i;
}
}
stack<int> s;
int p[maxn]; //p[i]表示长度为i的LIS结尾的最小值
memset(p,0,sizeof(p));
p[pos1[now]]=a[now];
for(int i=now-1;i>0;i--){
if(a[i]>=p[pos1[i]+1])
continue;
while(!s.empty()&&a[i]>=a[s.top()]) //关于单调性的选择,最开始我的想法是直接比较元素大小,不过不如题解中的比较pos直观,经实测比较pos或a均可
s.pop();
s.push(i);
p[pos1[i]]=a[i];
}
tot=0;
while(!s.empty()){
ans[++tot]=s.top();
s.pop();
}
ans[++tot]=now;
int last=now;
for(int i=now+1;i<=n;i++){
if(pos2[i]==pos2[last]-1&&a[i]<a[last]){
ans[++tot]=i;
last=i;
}
}
for(int i=1;i<=tot;i++){
printf("%d%c",ans[i],(i==tot?'\n':' '));
}
//计算字典序最大的情况,对称
//左边的从峰贪心地向左匹配即可,右边的需单调栈
mx=pos1[1]+pos2[1];
now=1;
for(int i=2;i<=n;++i){
if(pos1[i]+pos2[i]>=mx){
mx=pos1[i]+pos2[i];
now=i;
}
}
memset(p,0,sizeof(p)); //设为零,这样在检验时,如果pos值小2或更多,将会和0值比较,这个点本身也是不能采用的。
tot=0;
last=now;
for(int i=now-1;i>0;i--){
if(pos1[i]==pos1[last]-1&&a[i]<a[last]){
ans[pos1[now]-(++tot)]=i;
last=i;
}
}
ans[++tot]=now;
p[pos2[now]]=a[now];
for(int i=now+1;i<=n;i++){
if(a[i]>=p[pos2[i]+1])
continue;
while(!s.empty()&&a[i]>=a[s.top()])
s.pop();
p[pos2[i]]=a[i];
s.push(i);
}
while(!s.empty()){
ans[mx-(++tot)+pos1[now]]=s.top();
s.pop();
}
for(int i=1;i<=tot;i++)
printf("%d%c",ans[i],(i==tot?'\n':' '));
}
return 0;
}
线段树解法
待补充