【CF1647F】Madoka and Laziness
Cry_For_theMoon · · 题解
你小圆怎么 Div2 F 撞原 Div3 G 啊(恼)。我怎么半年前就做过场上还是不会啊(雾)
如果大家这个题有点困难可以先去学习核心部分的解法 CF1144G ,然后很快就懂了。
题意有点绕。就是说定义一个好的序列为先严格上升再严格下降。称最大值为拐点,记作
然后给你一个
这个题诈骗性质很强,因为你注意到
然后不失一般性我们可以设
这样整个数列应该被分成三部分:最左边的部分被拆成两个上升子序列,中间部分被拆成一个下降和一个上升的子序列,最右边的部分被拆成两个下降子序列。
这三个问题本质上是相近的,如果做过 CF1144G 这样的问题应该就立马会做了(除了我)。
我们就来分开来考虑三个问题好了:给定一个序列,能否拆成两个上升子序列(第一部分);或者一个上升一个下降的子序列(第二部分);或者两个下降子序列(第三部分)。
注意到第一部分和第三部分本质是相同的。我们只考虑第一部分和第二部分怎么做。
这部分很有趣,解决这类“二择”问题,关键是我们发现每个元素必定属于两个上升序列的一个,而上升/下降序列的话,我们只关注结尾的值,所以有这样一个想法:设
然后转移的思想是这样的:考虑
另外我们注意到,对于第一部分和第三部分
然后考虑把左中右三段拼接起来。左和中的拼接是容易的,我们只是把左边
这样,时间复杂度是
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
using namespace std;
const int MAXN=5e5+10,INF=2e9;
int n,ans,a[MAXN];
int f1[MAXN],f2[MAXN],g[2][MAXN];
void solve(){
int maxpos=1;rep(i,2,n)if(a[i]>a[maxpos])maxpos=i;
f1[0]=-INF;
rep(i,1,maxpos){
f1[i]=INF;
if(a[i-1]<a[i])f1[i]=min(f1[i],f1[i-1]);
if(f1[i-1]<a[i])f1[i]=min(f1[i],a[i-1]);
}
f2[n+1]=-INF;
per(i,n,maxpos){
f2[i]=INF;
if(a[i+1]<a[i])f2[i]=min(f2[i],f2[i+1]);
if(f2[i+1]<a[i])f2[i]=min(f2[i],a[i+1]);
}
g[0][maxpos]=f1[maxpos];
g[1][maxpos]=-INF;
rep(i,maxpos+1,n){
g[0][i]=INF;g[1][i]=-INF;
if(a[i-1]>a[i])g[0][i]=min(g[0][i],g[0][i-1]);
if(g[1][i-1]>a[i])g[0][i]=min(g[0][i],a[i-1]);
if(a[i-1]<a[i])g[1][i]=max(g[1][i],g[1][i-1]);
if(g[0][i-1]<a[i])g[1][i]=max(g[1][i],a[i-1]);
}
rep(i,maxpos+1,n){
if(g[1][i]>f2[i])ans++;
}
}
int main(){
cin>>n;
rep(i,1,n)cin>>a[i];
solve();
reverse(a+1,a+1+n);
solve();
cout<<ans;
return 0;
}