[JZOJ4296]有趣的有趣的家庭菜园

题目描述

n 棵植株排成一排,第 i 棵植株的高度、价值和拔除的花费分别为 hi pi ci
能够获得第 i 棵植株的价值,当且仅当这棵植株左边所有高度大于它的植株都被拔除,或者右边所有高度大于它的植株都被拔除。
最大化收益(价值和与花费和的差)。

3n105,1hi,pi,ci109

题目分析

我们观察获得价值的条件,按照 x 轴为编号, y 轴为高度,显然最终贡献价值的植株构成的图像就是一个开口朝下的单峰函数(可以特殊化成一条单调不上升/不下降的曲线)。
我们枚举函数最高点,然后求两边答案最大值之和即可。两边答案求法类似,我们只讨论从左到右。
fi 表示选择第 i 棵植株能造成的最大收益,先列出最暴力的 dp 方程:

fi=max{fjj<k<ihk>hick|j<ihjhi}

这样我们就能弄出 O(n2) 的算法。
考虑使用数据结构优化。可以发现,一个植株,对于后面的所有比它矮的植株的影响,就是 ci ,对于所有不低于它的植株,都可以转移答案。
因此我们以离散化的高度为下标,使用线段树某高度的答案。收取一个点的答案之后就对比它小的下标执行区间加 ci 操作,对不小于它的执行对 fi max 操作。
现在我们要按照时间顺序维护两种操作:区间加、区间 max ,这样怎么做呢?。
将一个点 x 的标记记为 max(mxx,a)+b
考虑区间加 c ,显然直接变成 max(mxx,a)+(b+c)
考虑区间对 c max ,推算一下 max(max(mxx,a)+b,c)=max(max(mxx,a),cb)+b
下传标记给儿子节点时先要下传 max 标记。还有注意 max 标记是否存在要使用布尔数组什么的标注一下,不然可能会和初值乱搞(如果你们维护得很好就自动无视这句话)。
然后就是时间复杂度为 O(nlog2n) 的算法。

代码实现

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

typedef long long LL;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch))
    {
        if (ch=='-')
            f=-1;
        ch=getchar();
    }
    while (isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

const int N=100500;

struct D
{
    int v,id;
}s[N];

bool operator<(D a,D b)
{
    return a.v<b.v;
}

struct segment_tree
{
    LL tag[N<<2][2],mx[N<<2];//0:add;1:max
    bool tg[N<<2];

    void init()
    {
        memset(tag,0,sizeof tag);
        memset(tg,0,sizeof tg);
        memset(mx,0,sizeof mx);
    }

    void merge(int x,LL y,bool tp)
    {
        if (!tp)
            tag[x][0]+=y;
        else
        {
            if (tg[x])
                tag[x][1]=max(tag[x][1],y-tag[x][0]);
            else
                tag[x][1]=y-tag[x][0];
            tg[x]=true;
        }
    }

    void mark(int x,LL y,bool tp)
    {
        merge(x,y,tp);
        if (!tp)
            mx[x]+=y;
        else
            mx[x]=max(mx[x],y);
    }

    void clear(int x,int l,int r)
    {
        if (l==r)
            return;
        if (tag[x][1])
        {
            mark(x<<1,tag[x][1],1),mark(x<<1|1,tag[x][1],1);
            tag[x][1]=0;
            tg[x]=false;
        }
        if (tag[x][0])
        {
            mark(x<<1,tag[x][0],0),mark(x<<1|1,tag[x][0],0);
            tag[x][0]=0;
        }
    }

    void update(int x)
    {
        mx[x]=max(mx[x<<1],mx[x<<1|1]);
    }

    LL query(int x,int y,int l,int r)
    {
        clear(x,l,r);
        if (l==r)
            return mx[x];
        int mid=l+r>>1;
        if (y<=mid)
            return query(x<<1,y,l,mid);
        else
            return query(x<<1|1,y,mid+1,r);
    }

    void change(int x,int st,int en,int l,int r,bool tp,LL edit)
    {
        clear(x,l,r);
        if (st==l&&en==r)
        {
            mark(x,edit,tp);
            return;
        }
        int mid=l+r>>1;
        if (en<=mid)
            change(x<<1,st,en,l,mid,tp,edit);
        else
            if (mid+1<=st)
                change(x<<1|1,st,en,mid+1,r,tp,edit);
            else
                change(x<<1,st,mid,l,mid,tp,edit),change(x<<1|1,mid+1,en,mid+1,r,tp,edit);
        update(x);
    }
}t;

int h[N],p[N],c[N],lab[N];
LL f[2][N];
int n,ind;
LL ans;

int main()
{
    freopen("herbary.in","r",stdin);
    freopen("herbary.out","w",stdout);
    n=read();
    for (int i=1;i<=n;i++)
        h[i]=read(),p[i]=read(),c[i]=read();
    for (int i=1;i<=n;i++)
        s[i].v=h[i],s[i].id=i;
    sort(s+1,s+1+n);
    ind=0;
    for (int i=1;i<=n;i++)
        lab[s[i].id]=(s[i].v==s[i-1].v)?ind:++ind;
    t.init();
    for (int i=1;i<=n;i++)
    {
        LL get=t.query(1,lab[i],1,ind);
        f[0][i]=get+p[i];
        if (lab[i]>1)
            t.change(1,1,lab[i]-1,1,ind,0,-c[i]);
        t.change(1,lab[i],ind,1,ind,1,f[0][i]);
    }
    for (int i=2;i<=n;i++)
        f[0][i]=max(f[0][i],f[0][i-1]);
    t.init();
    for (int i=n;i>=1;i--)
    {
        LL get=t.query(1,lab[i],1,ind);
        f[1][i]=get+p[i];
        if (lab[i]>1)
            t.change(1,1,lab[i]-1,1,ind,0,-c[i]);
        t.change(1,lab[i],ind,1,ind,1,f[1][i]);
    }
    for (int i=n-1;i>=1;i--)
        f[1][i]=max(f[1][i],f[1][i+1]);
    for (int i=1;i<n;i++)
        ans=max(f[0][i]+f[1][i+1],ans);
    ans=max(max(f[0][n],f[1][1]),ans);
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

你可能感兴趣的:(dp,线段树,OI)