zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    本地通知
    常用颜色RGB、灰度值
    AFNetworkingErrorDomain 错误解决方法
    给iOS开发新手送点福利,简述UIPikerView的属性和用法
    给iOS开发新手送点福利,简述UIImagePickerController的属性和用法
    给iOS开发新手送点福利,简述UIDatePicker的用法
    iOS线程开发小结
    给iOS开发新手送点福利,简述文本属性Attributes的用法
    给iOS开发新手送点福利,简述UIScrollView的属性和用法
    给iOS开发新手送点福利,简述UITableView的属性和用法
  • 原文地址:https://www.cnblogs.com/jasony/p/14049930.html
Copyright © 2011-2022 走看看