CF1647F Madoka and Laziness

题目大意

给你一个长度为 n n n的序列,序列上每个数互不相同,如果有一个序列中有满足 a 1 < a 2 < ⋯ < a i > ⋯ > a n a_1<a_2<\cdots<a_i>\cdots>a_n a1<a2<<ai>>an的关系的话,那么称这个序列是好的,把原序列分成两个 g o o d good good子序列,第一个子序列的最大值和第二个子序列最大值的不同对个数, ( a , b ) (a,b) (a,b) ( b , a ) (b,a) (b,a)视为同一对

题解

整个序列的最大值一定是其中一个子序列的最大值,设第一个最大值的位置为 i n d 1 ind1 ind1,我们可以假设第二个序列的最大值一定在右边(最后翻转序列再求一遍就可以得出答案),钦定第二个子序列的最大值的位置为 i n d 2 ind2 ind2,可以划分成三个部分,第一个是在 i n d 1 ind1 ind1左边需要找出两条递增的序列,第二个是在 i n d 1 ind1 ind1 i n d 2 ind2 ind2之间需要找出一条递减开头为 i n d 1 ind1 ind1,一条递增结尾为 i n d 2 ind2 ind2的序列,第三个在 i n d 2 ind2 ind2右边需要找到两条递减序列
先解决第一部分,考虑 d p dp dp,一开始想题时非常 n t nt nt,以为要设个二维 d p dp dp,第一维是一个序列的结尾,第二维是一个序列的结尾,想不到怎么优化,看了官方题解才明白,对于第 i i i个位置,前面的两条序列一定一条结尾是 i i i,所以可以只设一个一维 f i f_{i} fi,表示没有 i i i的那个序列结尾最小值是什么,第三个部分是类似的
第二个部分也可以用类似的方法解决, d p dp dp中讨论 i i i是在递增序列中还是在递减序列中,时间复杂度为 O ( n ) O(n) O(n)

c o d e code code

#include<cstdio>
#include<algorithm>
using namespace std;
void read(int &res)
{
	res=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
}
const int N=5e5+1000,inf=1e9+1;
int n,a[N+10],f1[N+10],f2[N+10],f3[N+10][2];
int solve()
{
	int ind=0,maxn=0,ans=0;
	for(int i=1;i<=n;i++)
		if(a[i]>maxn)
			maxn=a[i],ind=i;
	for(int i=1;i<=ind;i++) f1[i]=inf;
	f1[1]=0;
	for(int i=2;i<=ind;i++)
	{
		if(a[i]>a[i-1]) f1[i]=min(f1[i],f1[i-1]);
		if(a[i]>f1[i-1]) f1[i]=min(f1[i],a[i-1]);
	}
	for(int i=n;i>=ind;i--) f2[i]=inf;
	f2[n]=0;
	for(int i=n-1;i>=ind;i--)
	{
		if(a[i]>a[i+1]) f2[i]=min(f2[i],f2[i+1]);
		if(a[i]>f2[i+1]) f2[i]=min(f2[i],a[i+1]);
	}
	//f3[i][0]表示i在上升序列中下降序列的最大值,f3[i][1]表示i在下降序列中上升序列的最小值 
	for(int i=ind;i<=n;i++) f3[i][0]=0,f3[i][1]=inf;
	f3[ind][1]=f1[ind];
	for(int i=ind+1;i<=n;i++)
	{
		if(f3[i-1][1]<a[i]&&a[i-1]>f2[i]||a[i-1]<a[i]&&f3[i-1][0]>f2[i]) ans++;
		if(a[i-1]<a[i]) f3[i][0]=max(f3[i][0],f3[i-1][0]);
		if(f3[i-1][1]<a[i]) f3[i][0]=max(f3[i][0],a[i-1]);
		if(a[i-1]>a[i]) f3[i][1]=min(f3[i][1],f3[i-1][1]);
		if(f3[i-1][0]>a[i]) f3[i][1]=min(f3[i][1],a[i-1]);
	}
	return ans;
}
int main()
{
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	int ans=solve();
	for(int i=1;i<=n;i++)
	{
		if(i>=n-i+1) break;
		swap(a[i],a[n-i+1]);
	}
	printf("%d",ans+solve());
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值