AT1251 たのしいたのしい家庭菜園
注意!!! \(1-h_{i-1}\) 和 \(h_{i+1}-n\) 是可以有等于 \(h_i\) 的,翻译错了
为什么会有两个たのしい
Solution
考虑最后能够获得收入的位置,这些位置构成一个先上升再下降的序列。
那么我们可以枚举那个最高点,然后算出这个点左边递增的最大收入和右边递减的最大收入。
那么只考虑左边,右边同理即可。
我们设 \(f_i\) 表示拔完某些草(也就是已经减去 \(c\) )之后,没有比 \(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;
}