HDU多校第二场 1002 Beauty Of Unimodal Sequence —— LIS + 单调栈

题目链接:点我啊╭(╯^╰)╮

题目大意:

    长度为 n n 的序列,在保证最长的情况下
    求出字典序最小的最长单峰子序列
    和字典序最大的最长单峰子序列

解题思路:

    正向求一遍 L I S LIS ,再反向求一遍
     d p 1 dp1 为正向 L I S LIS d p 2 dp2 为反向 L I S LIS
    那么最长的长度就是 m x = m a x ( d p 1 [ i ] + d p 2 [ i ] ) mx = max(dp1[i]+dp2[i])
    在序列中可能出现多个峰为 m x mx


    字典序最小的峰一定是第一个峰
    那么对于这个峰值 m x mx ,往前遍历一定是贪心取较小的合法子序列
    往后遍历就按顺序取第一个合法子序列
    那么往前遍历的过程怎么实现贪心的思想呢?

    假设枚举到了 a [ i ] a[i] 这个数, d p 1 [ i ] dp1[i] 表示的就是这个数在 L I S LIS 中的位置 p o s pos
    这个数可以放进去,首先要满足在 p o s + 1 pos+1 位置上的数要 a [ i ] >a[i]
    若 a [ i ] a[i] 这个数可以放入,那么放入后一定存在合法的序列
     [ 1 , i 1 ] [1,i-1] 的数可以放满位置在 [ 1 , p o s 1 ] [1,pos-1] 的所有数

    那么要求字典序最小, a [ i ] a[i] 这个数一定是 p o s pos 这个位置的最佳选择
    所以就清空 p o s ≥pos 上的所有数,这个过程可以用单调栈维护
    往后遍历就不断取第一个合法的数字即可


    字典序最大的峰一定是最后一个峰
    然后往前遍历取第一个合法子序列
    往后遍历贪心取较大的合法子序列

核心: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');
	}
}
发布了221 篇原创文章 · 获赞 220 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/102800822