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

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

洛谷传送门 AT传送门

为什么会有两个たのしい

Solution

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

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

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

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

那么可以得到转移方程:

[f_i=maxlimits_{1leq j <i And h_jleq h_i}{f_j-sum_{j<k<iAnd h_k>h_i} c_k}+p_i ]

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

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


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

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

小细节:因为 (h_ileq 10^9,nleq 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
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/jasony/p/14049930.html