【动态规划】【线段树】P8051 [ZYOI Round1] Bird/鸟

分析

先不考虑瞬移功能,设 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]jrp[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值