Two Merged Sequences(CF 1144 G)

本文探讨了一种将序列完全划分为上升和下降子序列的问题,提出了一种巧妙的O(n)DP算法解决方案,通过定义状态和转移方程,避免了传统线段树方法的复杂性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在做其它题的时候脑补到过这个题意,没想到还真有这样的题

题目相关

链接

题目大意

给一个序列,问是否能将这个序列完全划分成一个上升子序列和下降子序列

数据范围

n ≤ 2 ⋅ 1 0 5 n\le2·10^5 n2105

题解

有个不错的思路:
找到最大值,讨论他是上升序列的最后一个元素还是下降序列的第一个元素
如果是上升序列的最后一个元素,那么需要满足两个条件:
1.其后面的元素都是在下降序列中的
2.其前面的元素的下降序列中的最后一个元素大于后面的元素
如果这是下降序列的第一个元素,将数组翻转即可
我们发现,对于第一个条件的满足与否,就可以判断是哪种情况

  • 两边都不满足:无解
  • 有一边满足:可能是这种情况,转化子问题递归计算
  • 两边都满足:直接解决问题

递归计算时,我们发现有一个额外的限制,我们要先把这个限制弄掉
实际操作中,限制可以直接把连续的一段删除并产生新的限制,若新的限制无效了,那么就是子问题了,再递归计算即可
以上的做法可能要用到线段树,并且要在线段树上二分之类的,不是很好写
而且复杂度为 O ( n l o g n ) \mathcal O(nlogn) O(nlogn)


然后,这里隆重推出 O ( n ) \mathcal O(n) O(n)的dp做法
看来是我dp没学好,咋开始没想到呢?
我们定义状态:
f i , 0 f_{i,0} fi,0为第 i i i个数在下降子序列上,此时上升子序列最大值的最小值
f i , 1 f_{i,1} fi,1为第 i i i个数在上升子序列上,此时下降子序列最小值的最大值
转移的话直接讨论一下上个状态和下个状态放哪种就好了
然后就可以直接判定
输出方案的话记录每一个状态的前继状态即可
清真+好写+复杂度低

代码

#include<bits/stdc++.h>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
const int inf=0x7f7f7f7f;
int n,a[200001],opt[200001];
int f[200001][2],g[200001][2];
int main()
{
	read(n);
	for(rg int i=1;i<=n;i++)read(a[i]);
	f[1][0]=-inf,f[1][1]=inf;
	for(rg int i=2;i<=n;i++)
	{
		f[i][0]=inf,f[i][1]=-inf;
		if(a[i-1]>a[i]&&f[i][0]>f[i-1][0])
		{
			f[i][0]=f[i-1][0];
			g[i][0]=0;
		}
		if(f[i-1][0]<a[i]&&f[i][1]<a[i-1])
		{
			f[i][1]=a[i-1];
			g[i][1]=0;
		}
		if(a[i-1]<a[i]&&f[i][1]<f[i-1][1])
		{
			f[i][1]=f[i-1][1];
			g[i][1]=1;
		}
		if(f[i-1][1]>a[i]&&f[i][0]>a[i-1])
		{
			f[i][0]=a[i-1];
			g[i][0]=1;
		}
	}
	if(f[n][0]!=inf)
	{
		puts("YES");
		for(rg int i=n,op=0;i>=1;op=g[i][op],i--)opt[i]=op;
		for(rg int i=1;i<=n;i++)print(opt[i]^1),putchar(' ');
		return 0;
	}
	if(f[n][1]!=-inf)
	{
		puts("YES");
		for(rg int i=n,op=1;i>=1;op=g[i][op],i--)opt[i]=op;
		for(rg int i=1;i<=n;i++)print(opt[i]^1),putchar(' ');
		return 0;
	}
	puts("NO");
	return 0;
}

总结

挺巧妙的一道题
有时候换种做法说不定会好写很多

在编程和技术语境中,“merged” 是英文单词 “merge” 的过去式和过去分词形式,意思是 **“合并”**。它通常用于以下几种场景: --- ### 1. **Git / 版本控制中的 Merged** 在 Git 中,`merged` 表示两个分支的代码被合并在一起。 例如: ```bash git merge feature-branch ``` 这条命令会将 `feature-branch` 分支的内容合并到当前分支(如 `main` 或 `master`)中。如果成功,就会提示类似: ``` Merge commit created. ``` ✅ 示例: ```bash git checkout main git merge dev ``` 这表示你把 `dev` 分支的更改合并到了 `main` 分支中。 --- ### 2. **数据结构与算法中的 Merged** 在算法中,尤其是归并排序(Merge Sort)或合并两个有序数组时,`merged` 指的是两个或多个数据结构(如数组、链表)被合并成一个整体。 ✅ 示例:合并两个有序数组 ```python def merge_sorted_arrays(a, b): return sorted(a + b) a = [1, 3, 5] b = [2, 4, 6] merged = merge_sorted_arrays(a, b) print("Merged array:", merged) ``` 输出: ``` Merged array: [1, 2, 3, 4, 5, 6] ``` --- ### 3. **前端开发中的 Merged** 在前端框架(如 React、Vue)中,有时你会看到“props merged”,指的是组件接收到的属性(props)被合并处理,比如默认值和传入值进行合并。 --- ### 4. **对象或配置合并** 在 JavaScript 等语言中,常见使用 `Object.assign()` 或展开运算符来合并对象: ```javascript const defaultConfig = { theme: 'light', debug: false }; const userConfig = { debug: true }; const mergedConfig = { ...defaultConfig, ...userConfig }; console.log(mergedConfig); ``` 输出: ``` { theme: 'light', debug: true } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值