题目链接:点我啊╭(╯^╰)╮
题目大意:
长度为
的序列,在保证最长的情况下
求出字典序最小的最长单峰子序列
和字典序最大的最长单峰子序列
解题思路:
正向求一遍
,再反向求一遍
为正向
,
为反向
那么最长的长度就是
在序列中可能出现多个峰为
字典序最小的峰一定是第一个峰
那么对于这个峰值
,往前遍历一定是贪心取较小的合法子序列
往后遍历就按顺序取第一个合法子序列
那么往前遍历的过程怎么实现贪心的思想呢?
假设枚举到了
这个数,
表示的就是这个数在
中的位置
这个数可以放进去,首先要满足在
位置上的数要
若
这个数可以放入,那么放入后一定存在合法的序列
即
的数可以放满位置在
的所有数
那么要求字典序最小,
这个数一定是
这个位置的最佳选择
所以就清空
上的所有数,这个过程可以用单调栈维护
往后遍历就不断取第一个合法的数字即可
字典序最大的峰一定是最后一个峰
然后往前遍历取第一个合法子序列
往后遍历贪心取较大的合法子序列
核心:LIS的单调性
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;
int n, a[maxn], dp[maxn];
int dp1[maxn], dp2[maxn];
int tp, q[maxn], h[maxn];
int cnt, ans[maxn];
int main() {
while(~scanf("%d", &n)){
memset(dp, 0x3f, sizeof(dp));
for(int i=1; i<=n; i++){
scanf("%d", a+i);
dp1[i] = lower_bound(dp+1, dp+n+1, a[i]) - dp;
dp[dp1[i]] = a[i];
}
memset(dp, 0x3f, sizeof(dp));
for(int i=n; i; i--){
dp2[i] = lower_bound(dp+1, dp+1+n, a[i]) - dp;
dp[dp2[i]] = a[i];
}
int id = 1, mx = dp1[1] + dp2[1];
for(int i=2; i<=n; i++)
if(dp1[i]+dp2[i] > mx){
id = i;
mx = dp1[i] + dp2[i];
}
deb(mx);
memset(h, 0, sizeof(h));
q[tp = 1] = id, h[dp1[id]] = a[id], cnt = 0;
for(int i=id-1; i; i--){
if(a[i] >= h[dp1[i]+1]) continue;
while(tp && dp1[i]>=dp1[q[tp]]) tp--;
q[++tp] = i, h[dp1[i]] = a[i];
}
while(tp) ans[++cnt] = q[tp--];
for(int i=id+1; i<=n; i++)
if(dp2[i]==dp2[ans[cnt]]-1 && a[i]<a[ans[cnt]])
ans[++cnt] = i;
for(int i=1; i<=cnt; i++) printf("%d%c", ans[i], i<cnt ? ' ' : '\n');
//------------------------------------------------------------
for(int i=2; i<=n; i++)
if(dp1[i]+dp2[i] >= mx){
id = i;
mx = dp1[i] + dp2[i];
}
q[tp = 1] = id, cnt = 0;
for(int i=id-1; i; i--)
if(dp1[i]==dp1[q[tp]]-1 && a[i]<a[q[tp]]) q[++tp] = i;
while(tp) ans[++cnt] = q[tp--];
memset(h, 0, sizeof(h));
h[dp2[id]] = a[id];
for(int i=id+1; i<=n; i++){
if(a[i] >= h[dp2[i]+1]) continue;
while(cnt && dp2[i]>=dp2[ans[cnt]]) cnt--;
ans[++cnt] = i, h[dp2[i]] = a[i];
}
for(int i=1; i<=cnt; i++) printf("%d%c", ans[i], i<cnt ? ' ' : '\n');
}
}