[codevs3289]花匠 线段树优化dp

来源:互联网 发布:mac图片如何新建文件夹 编辑:程序博客网 时间:2024/05/20 06:41

题目←

大意:

求最长波动序列

设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 1061109567using 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;}

转化类似于权值线段树?

原创粉丝点击