题意:给你一个序列,让你找长度最长的字典序最小和最大的单峰序列,单峰序列就是满足先增后降的序列。
思路:先正着求一遍LIS,再反着求一遍LIS,然后用单调栈来模拟。
求字典序最小的话,首先找到第一个顶峰,然后往前找递减的序列中下标较小的,往后就依次找,这样能保证字典序最小。
最大的话找到最后一个顶峰,往前是依次找,往后是找LIS中下标大的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 10;
const int inf = 0x3f3f3f3f;
int d[maxn], p[maxn], ans[maxn];
int a[maxn], pos1[maxn], pos2[maxn];
stack<int> s;
int main()
{
int n;
while(~scanf("%d", &n))
{
memset(d, 0x3f, (n + 5) * sizeof(int));
d[0] = 0;
for(int i = 1; i <= n; ++i) //正着求一遍LIS
{
scanf("%d", &a[i]);
pos1[i] = lower_bound(d + 1, d + n + 1, a[i]) - d;
d[pos1[i]] = a[i];
}
memset(d, 0x3f, (n + 5) * sizeof(int));
d[0] = 0;
for(int i = n; i > 0; --i) //反着求一遍LIS
{
pos2[i] = lower_bound(d + 1, d + n + 1, a[i]) - d;
d[pos2[i]] = a[i];
}
//找字典序最小的
int now = 1, Max = pos1[1] + pos2[1], tot = 0;
for(int i = 2; i <= n; ++i) //先找到第一个顶峰
{
if(pos1[i] + pos2[i] > Max)
{
Max = pos1[i] + pos2[i];
now = i; //记录下标
}
}
p[pos1[now]] = a[now];
for(int i = now - 1; i > 0; --i) //往前找到满足LIS的下标
{
if(a[i] >= p[pos1[i] + 1]) continue;
while(!s.empty() && pos1[s.top()] <= pos1[i]) s.pop();
s.push(i);
p[pos1[i]] = a[i];
}
while(!s.empty())
{
ans[++tot] = s.top();
s.pop();
}
ans[++tot] = now;
for(int i = now + 1; i <= n; ++i) //往后找严格递减的
{
if(pos2[i] == pos2[ans[tot]] - 1 && a[i] < a[ans[tot]])
ans[++tot] = i;
}
for(int i = 1; i < tot; ++i)
printf("%d ", ans[i]);
printf("%d\n", ans[tot]);
//找到字典序最大的
now = 1, Max = pos1[1] + pos2[1], tot = 0;
for(int i = 2; i <= n; ++i) //找到最后一个顶峰
{
if(pos1[i] + pos2[i] >= Max)
{
Max = pos1[i] + pos2[i];
now = i;
}
}
memset(p, 0x3f, sizeof(p));
s.push(now);
for(int i = now - 1; i > 0; --i) //往前找递减的,也就是从头递增的
if(pos1[i] == pos1[s.top()] - 1 && a[i] < a[s.top()])
s.push(i);
while(!s.empty())
{
ans[++tot] = s.top();
s.pop();
}
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;
}