HDU 6592 (LIS+输出字典序最大最小)
题意:给你一个序列,让你找长度最长的字典序最小和最大的单峰序列,单峰序列就是满足先增后降的序列。
思路:先正着求一遍LIS,再反着求一遍LIS,然后用单调栈来模拟。
求字典序最小的话,首先找到第一个顶峰,然后往前找递减的序列中下标较小的,往后就依次找,这样能保证字典序最小。
最大的话找到最后一个顶峰,往前是依次找,往后是找LIS中下标大的。
#include<bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f const int N = 3e5+10; int d[N],pos1[N],pos2[N],a[N],p[N],ans[N]; int main(){ int n; while(~scanf("%d",&n)){ for(int i=0 ; i<=n ; i++) d[i]=INF,p[i]=INF; d[0]=0; for(int i=1 ; i<=n ; i++){///正向LIS scanf("%d",&a[i]); pos1[i]=lower_bound(d+1,d+1+n,a[i])-d; d[pos1[i]]=a[i]; } for(int i=0 ; i<=n ; i++) d[i]=INF; d[0]=0; for(int i=n ; i>=1 ; i--){///反向LIS pos2[i]=lower_bound(d+1,d+1+n,a[i])-d; d[pos2[i]]=a[i]; } int now=1,mx=pos1[1]+pos2[1],tot=0;///找到最小秃顶 for(int i=2 ; i<=n ; i++) { if(pos1[i]+pos2[i]>mx){ mx=pos1[i]+pos2[i]; now=i; } } ///找字典序最小就是正找小反找大(仔细一想是这么一回事) stack<int>st; p[pos1[now]]=a[now];/// pos1[now]--长度的尾巴是a[now] for(int i=now-1;i>=1;i--){ if(a[i]>=p[pos1[i]+1]) continue;///排除不合格的条件 -> 6 5 (把6排除) while(!st.empty()&&pos1[st.top()]<=pos1[i]) st.pop(); ///在可选的,选择下标小的 st.push(i); p[pos1[i]]=a[i]; } while(!st.empty()){ ans[++tot]=st.top(); st.pop(); } ans[++tot]=now; ///反向最大就是一直调下去 for(int i=now+1 ; i<=n ; i++){ if(pos2[i]==pos2[ans[tot]]-1&&a[ans[tot]]>a[i]) ans[++tot]=i; } for(int i=1;i<tot;i++) printf("%d ",ans[i]); printf("%d\n",ans[tot]); now=1; mx=pos1[1]+pos2[1]; tot=0; for(int i=2;i<=n;i++){///找到最大秃顶 if(pos1[i]+pos2[i]>=mx){ mx=pos1[i]+pos2[i]; now=i; } } ///找字典序最大就是正找大反找小 st.push(now); for(int i=now-1;i>=1;i--){ if(pos1[i]==pos1[st.top()]-1&&a[i]<a[st.top()]){ st.push(i); } } while(!st.empty()){ ans[++tot]=st.top();st.pop(); } for(int i=0 ; i<=n ; i++) p[i]=0; p[pos2[now]]=a[now]; for(int i=now+1;i<=n;i++){ if(a[i]>=p[pos2[i]+1]) continue; while(tot>0&&pos2[i]>=pos2[ans[tot]]) tot--; ans[++tot]=i; p[pos2[i]]=a[i]; } for(int i=1;i<tot;i++) printf("%d ",ans[i]); printf("%d\n",ans[tot]); } return 0; }