[codevs3289]花匠 线段树优化dp

题目←

大意:

求最长波动序列

设f[i]为dp到第i位、以波峰结尾的最长波动序列,
设g[i]为dp到第j位、以波谷结尾的最长波动序列。

则f[i]可以由满足h[j] < h[i]的g[j]转来,
g[i]可以由满足h[j] > h[i]的f[j]转来。

则当转移f[i]的时候,需要找到之前出现过的、高度小于h[i]的且g[j]最大的点
用h[i]的值域建树,maxf/maxg[h[i]]表示以高度h[i]结尾的最大f[i]/g[i]
每获得一个新的f[i]或g[i],更新maxf/maxg[h[i]]。
那转移f[i]的时候,询问(0,h[i] - 1)区间中最大的maxg就可以了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define L(w) w << 1
#define R(w) w << 1|1
#define INF 1061109567
using namespace std;
const int MAXN = 200000 + 50;
int L[MAXN << 2],R[MAXN << 2],maxf[MAXN << 2],maxg[MAXN << 2];
int n,h[MAXN],maxn;
void update(int w)
{
    maxf[w] = max(maxf[L(w)],maxf[R(w)]);
    maxg[w] = max(maxg[L(w)],maxg[R(w)]);
}
void build_tree(int w,int l,int r)
{
    L[w] = l;R[w] = r;
    if(l == r)
    {
        maxf[w] = -INF;
        maxg[w] = -INF;
        return;
    }
    int mid = L[w] + R[w] >> 1;
    build_tree(L(w),l,mid);
    build_tree(R(w),mid + 1,r);
    update(w);
}
int f[MAXN],g[MAXN];
void add(int w,int loc,int x,int ch)
{
    if(L[w] == R[w])
    {
        if(ch)
        {
            maxf[w] = max(maxf[w],x);
        }
        else
        {
            maxg[w] = max(maxg[w],x);
        }
        return;
    }
    int mid = L[w] + R[w] >> 1;
    if(loc <= mid)add(L(w),loc,x,ch);
    else add(R(w),loc,x,ch);
    update(w);
}
int ask(int w,int l,int r,int v)
{
    if(L[w] == l && R[w] == r)
    {
        return v ? maxg[w] : maxf[w];
    }
    int mid = L[w] + R[w] >> 1;
    if(r <= mid)return ask(L(w),l,r,v);
    else if(l > mid)return ask(R(w),l,r,v);
    else return max(ask(L(w),l,mid,v),ask(R(w),mid + 1,r,v));
}
int ans;
int tmp[MAXN];
int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++)
        scanf("%d",&h[i]),tmp[i] = h[i];
    sort(tmp + 1,tmp + n + 1);
    int tot = unique(tmp + 1,tmp + n + 1) - tmp - 1;
    for(int i = 1;i <= n;i ++)
        h[i] = lower_bound(tmp + 1,tmp + tot + 1,h[i]) - tmp,maxn = max(maxn,h[i]);
    build_tree(1,0,maxn + 1);
    f[1] = g[1] = 1;
    add(1,h[1],f[1],1);
    add(1,h[1],g[1],0);
    for(int i = 2;i <= n;i ++)
    {
        f[i] = ask(1,0,h[i] - 1,1) + 1;
        g[i] = ask(1,min(maxn,h[i] + 1),maxn,0) + 1;
        add(1,h[i],f[i],1);
        add(1,h[i],g[i],0);
        ans = max(ans,max(f[i],g[i]));
    }
    printf("%d",ans);
    return 0;
}

转化类似于权值线段树?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值