前言
在做其它题的时候脑补到过这个题意,没想到还真有这样的题
题目相关
题目大意
给一个序列,问是否能将这个序列完全划分成一个上升子序列和下降子序列
数据范围
n ≤ 2 ⋅ 1 0 5 n\le2·10^5 n≤2⋅105
题解
有个不错的思路:
找到最大值,讨论他是上升序列的最后一个元素还是下降序列的第一个元素
如果是上升序列的最后一个元素,那么需要满足两个条件:
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;
}
总结
挺巧妙的一道题
有时候换种做法说不定会好写很多