给你一个长度为 n n n的序列,序列上每个数互不相同,如果有一个序列中有满足 a 1 < a 2 < ⋯ < a i > ⋯ > a n a_1
整个序列的最大值一定是其中一个子序列的最大值,设第一个最大值的位置为 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)
#include
#include
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;
}