分析
先不考虑瞬移功能,设
f
i
f_i
fi表示i位置上的最多的飞行次数,那么我们可以线通过单调栈预处理出来每个位置 i ,左侧和右侧第一个比它高的位置,分别记为lp[i]和rp[i],那么转移方程为
f
i
=
m
a
x
{
f
j
+
1
∣
l
p
[
i
]
≤
j
≤
r
p
[
i
]
}
f_i=max\{f_j+1 \ | \ lp[i]\le j \le rp[i] \}
fi=max{fj+1 ∣ lp[i]≤j≤rp[i]}
注意转移顺序要按照从低到高,这样就能保证每个需要的位置都已经求出来了
这里的单修,区间查询max,使用的是zkw线段树,常数更小。
第一次写zkw线段树,主要就是把建一棵满二叉树,然后把递归操作转换为循环,写起来也比较简单,但是zkw处理不了运算有优先级顺序的问题,比如同时维护乘法和加法的那种
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
int n,a[maxn],id[maxn],k;
int mp[maxn],lp[maxn],rp[maxn];
int st[maxn],top;
bool cmp(int x,int y)
{
return a[x]<a[y];
}
int tr[maxn<<2],N,M;
int f[maxn];
void update(int x,int val)
{
x+=N; tr[x]=max(tr[x],val);
while(x)
{
x>>=1;
tr[x]=max(tr[x],val);
}
}
int query(int l,int r)
{
int res=0;
for(l=l+N-1,r=r+N+1;l^r^1;l>>=1,r>>=1)
{
if(~l&1) res=max(res,tr[l^1]);
if( r&1) res=max(res,tr[r^1]);
}
return res;
}
int pre[maxn];
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
while(top && a[st[top]]<a[i]) top--;
lp[i]=st[top];
st[++top]=i;
}
top=0; st[0]=n+1;
for(int i=n;i>=1;i--)
{
while(top && a[st[top]]<a[i]) top--;
rp[i]=st[top];
st[++top]=i;
}
for(int i=1;i<=n;i++) id[i]=i;
sort(id+1,id+n+1,cmp);
for(N=1,M=0;N<=n+1;N<<=1,M++);
for(int i=1;i<=n;i++)
{
int j=id[i];
if(lp[j]+1==rp[j]-1) continue;
f[j]=query(lp[j]+1,rp[j]-1)+1;
update(j,f[j]);
}
for(int i=1;i<=n;i++) pre[i]=max(pre[i-1],f[id[i]]);
// for(int i=1;i<=n;i++) printf("%d %d %d\n",lp[i],rp[i],pre[i]);
ll res=f[1];
sort(a+1,a+n+1);
for(int i=1;i<=k;i++)
{
scanf("%d",&mp[i]);
int pos=upper_bound(a+1,a+n+1,mp[i])-a-1;
res+=pre[pos];
}
printf("%lld",res);
return 0;
}