P1970 花匠 [权值线段树优化dp, NOIp2003, Y]

花 匠 花匠


D e s c r i p t i o n \mathcal{Description} Description
见标题链接.


最 初 想 法 最初想法
既然有两种情况, 就一个个解决吧, 先第二种情况,
F [ i , 0 / 1 ] F[i,0/1] F[i,0/1] 表示前 i i i 个能够选择出的最多花盆, 0 0 0 表示 i i i位置作为 低点, 1 1 1 表示 i i i位置作为 高点.
初值 F [ i , 1 ] = 1 , F [ i , 0 ] = 0 F[i,1]=1,F[i,0]=0 F[i,1]=1,F[i,0]=0 .

H [ j ] &gt; H [ i ] :    F [ i , 0 ] = m a x ( F [ j , 1 ] + 1 ) H [ j ] &lt; H [ i ] :    F [ i , 1 ] = m a x ( F [ j , 0 ] + 1 ) H[j]&gt;H[i]:\ \ F[i,0]=max(F[j,1]+1) \\ H[j]&lt;H[i]:\ \ F[i,1]=max(F[j,0]+1) H[j]>H[i]:  F[i,0]=max(F[j,1]+1)H[j]<H[i]:  F[i,1]=max(F[j,0]+1)

A n s = m a x i ∈ [ 1 , N ] ( F [ i , 1 ] ) Ans=max_{i∈[1,N]}(F[i,1]) Ans=maxi[1,N](F[i,1])

先提交一下 O ( N 2 ) O(N^2) O(N2) 暴力, 检验正确性, 结果 40 p t s 40pts 40pts 爆炸.

原来题目要求的偶数位为 最低点/最高点 可以当做选出最后一个盆栽啊!

于是
初值 F [ i , 1 ] = F [ i , 0 ] = 1 F[i,1]=F[i,0]=1 F[i,1]=F[i,0]=1,
A n s = m a x i ∈ [ 1 , N ] ( F [ i , 1 ] , F [ i , 0 ] ) Ans=max_{i∈[1,N]}(F[i,1],F[i,0]) Ans=maxi[1,N](F[i,1],F[i,0])
也不用一个一个情况讨论了, 因为相当于求的是 最长波浪 了.

改了之后就有 80 p t s 80pts 80pts 了, Luogu跑的真快 .


正 解 部 分 正解部分

使用权值线段树优化为 O ( N l o g N ) O(NlogN) O(NlogN) 即可.

#include<bits/stdc++.h>
#define reg register

const int maxn = 100005;

int N;
int Ans;
int H[maxn];
int F[maxn][2];

struct Node{ int l, r, max_v; } T[2][(int)(4e6)+5];

void Build(int k, int l, int r){
        T[0][k].l = T[1][k].l = l, T[0][k].r = T[1][k].r = r;
        if(l == r){
                T[1][k].max_v = T[0][k].max_v = 0;
                return ;
        }
        int mid = l+r >> 1;
        Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
        T[0][k].max_v = T[1][k].max_v = 0;
}

void Modify(int k, int pos, int val, int opt){
        int l = T[opt][k].l, r = T[opt][k].r;
        if(l == r){ T[opt][k].max_v = val; return ; }
        int mid = l+r >> 1;
        if(pos <= mid) Modify(k<<1, pos, val, opt);
        else Modify(k<<1|1, pos, val, opt);
        T[opt][k].max_v = std::max(T[opt][k<<1].max_v, T[opt][k<<1|1].max_v);
}

int Query(int k, int Ql, int Qr, int opt){
        if(Ql > Qr) return 0;
        int l = T[opt][k].l, r = T[opt][k].r;
        if(Ql <= l && r <= Qr) return T[opt][k].max_v;
        int mid = l+r >> 1;
        if(Qr <= mid) return Query(k<<1, Ql, Qr, opt);
        else if(Ql >= mid+1) return Query(k<<1|1, Ql, Qr, opt);
        return std::max(Query(k<<1, Ql, Qr, opt), Query(k<<1|1, Ql, Qr, opt));
}

int main(){
        scanf("%d", &N);
        int Lim = 1;
        for(reg int i = 1; i <= N; i ++) scanf("%d", &H[i]), Lim = std::max(H[i], Lim);
        Build(1, 0, Lim);
        for(reg int i = 1; i <= N; i ++){
                F[i][0] = Query(1, 0, H[i]-1, 1) + 1;
                F[i][1] = Query(1, H[i]+1, Lim, 0) + 1;
                Modify(1, H[i], F[i][0], 0);
                Modify(1, H[i], F[i][1], 1);
                Ans = std::max(Ans, F[i][1]);
                Ans = std::max(Ans, F[i][0]);
        }
        printf("%d\n", Ans);
        return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值