AT1251 たのしいたのしい家庭菜園

注意!!! \(1-h_{i-1}\)\(h_{i+1}-n\) 是可以有等于 \(h_i\) 的,翻译错了

洛谷传送门 AT传送门

为什么会有两个たのしい

Solution

考虑最后能够获得收入的位置,这些位置构成一个先上升再下降的序列。

那么我们可以枚举那个最高点,然后算出这个点左边递增的最大收入和右边递减的最大收入。

那么只考虑左边,右边同理即可。

我们设 \(f_i\) 表示拔完某些草(也就是已经减去 \(c\) )之后,没有比 \(i\) 更高的草,剩余这些草的最大总收益。

那么可以得到转移方程:

\[f_i=\max\limits_{1\leq j <i \And h_j\leq h_i}\{f_j-\sum_{j<k<i\And h_k>h_i} c_k\}+p_i \]

\(\sum\limits_{j<k<i\And h_k>h_i}c_k\) 表示 \(j\)\(i\) 这段中,只需要删去 \(h_k>h_i\) 的那一部分。

这样的时间复杂度是 \(O(n^3)\) 的,所以需要优化。


因为 \(h_j\leq h_i<h_k\) ,所以可以看作 \(h_k\)\(h_j\) 有一个 \(-c_k\) 的贡献,进而 \(h_k\) 对所有 \(h_j<h_k\)\(j\) 的都有一份贡献,所以考虑以高度为下标开一颗线段树,维护区间最大值。

具体操作:对于 \(i\) ,每次查询 \(\max[0,h_i]\) ,可得 \(f_i=\max[0,h_i]+p_i\) ,然后拿 \(f_i\) 更新 \(h_i\) 位置,并对于 \([0,h_i-1]\) 减去 \(c_i\)

可以看出 \(\max[0,h_i]\) 就是转移方程中的 \(\max\limits_{1\leq j <i\And h_j\leq h_i}\{f_j-\sum\limits_{j<k<i\And h_k>h_i} c_k\}\) 。因为当我们在操作 \(j\) 之后,每一个 \(c_k(h_k>h_j)\) 我们都已经减去。那么到了 \(i\) 的时候, \([0,h_i]\) 上的点就是我们要求的,这个时候查询 \(\max\) 即可。

那这个题的大体思路就没了。

小细节:因为 \(h_i\leq 10^9,n\leq 10^5\) ,所以如果以高度为下标建树是不行的,记得离散化。

Code

#include<bits/stdc++.h>
#define ll long long
#define ls rt<<1
#define rs rt<<1|1

using namespace std;
const int N=1e5+10;
const ll INF=1e18;
int n,h[N],p[N],c[N],buk[N],lsh;
ll f[N],g[N],mx[N<<2],lz[N<<2],ans;

inline 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^48);ch=getchar();}
    return x*f;
}

void build(int rt,int l,int r){
    mx[rt]=-INF; lz[rt]=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(ls,l,mid); build(rs,mid+1,r);
}

inline void pushdown(int rt){
    if(!lz[rt]) return ;
    mx[ls]+=lz[rt]; lz[ls]+=lz[rt];
    mx[rs]+=lz[rt]; lz[rs]+=lz[rt];
    lz[rt]=0;
}

void update(int rt,int l,int r,int L,int R,ll v){
    if(L<=l&&r<=R){
        mx[rt]+=v; lz[rt]+=v;
        return ;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(L<=mid) update(ls,l,mid,L,R,v);
    if(R>mid) update(rs,mid+1,r,L,R,v);
    mx[rt]=max(mx[ls],mx[rs]);
}

ll query(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R) return mx[rt];
    pushdown(rt);
    int mid=(l+r)>>1;
    ll res=-INF;
    if(L<=mid) res=max(res,query(ls,l,mid,L,R));
    if(R>mid) res=max(res,query(rs,mid+1,r,L,R));
    return res;
}

void insert(int rt,int l,int r,int p,ll v){
    if(l==r){
        mx[rt]=max(v,mx[rt]);
        return ;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(p<=mid) insert(ls,l,mid,p,v);
    else insert(rs,mid+1,r,p,v);
    mx[rt]=max(mx[ls],mx[rs]);
}

inline void work(ll *f){
    build(1,0,lsh); insert(1,0,lsh,0,0);
    for(int i=1;i<=n;i++){
        f[i]=query(1,0,lsh,0,h[i])+p[i];
        insert(1,0,lsh,h[i],f[i]);
        update(1,0,lsh,0,h[i]-1,-c[i]);
    }
}

int main(){
    n=read();
    for(int i=1;i<=n;i++)
        h[i]=read(),p[i]=read(),c[i]=read(),buk[++lsh]=h[i];
    sort(buk+1,buk+lsh+1);
    lsh=unique(buk+1,buk+lsh+1)-buk-1;
    for(int i=1;i<=n;i++)
        h[i]=lower_bound(buk+1,buk+lsh+1,h[i])-buk;
    work(f);
    reverse(h+1,h+n+1); reverse(p+1,p+n+1); reverse(c+1,c+n+1);
    work(g);
    reverse(g+1,g+n+1); reverse(p+1,p+n+1);
    for(int i=1;i<=n;i++)
        ans=max(ans,f[i]+g[i]-p[i]);
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-11-27 19:34  jasony_sam  阅读(88)  评论(0编辑  收藏  举报