职业经营家庭菜园的JOI君每年在自家的田地中种植一种叫做IOI草的植物。IOI草的种子在冬天被播下,春天会发芽并生长至一个固定的高度。到了秋天,一些IOI草会结出美丽的果实,并被收获,其他的IOI草则会在冬天枯萎。
JOI君的田地沿东西方向被划分为N个区域,从西侧开始的第i个区域中种植着IOI草i。在第i个区域种植的IOI草,在春天的时候高度会生长至Hi,此后便不再生长。如果IOI草i会结出果实,那么将会获得Pi的收益,否则没有收益。
春天到了,查看田地样子的JOI君决定拔掉一些种植的IOI草,使利益最大化。拔掉IOI草i需要Ci的花销,拔掉的IOI草会立刻枯萎。IOI草只能在春天被拔掉,夏天和秋天不能拔掉IOI草。
IOI草是一种非常依靠阳光的植物,如果在夏天某个区域的IOI草的东侧和西侧都有比它高的IOI草存在,那么这株IOI草在秋天便不会结出果实。换句话说,为了让没有被拔掉的IOI草i在秋天结出果实,到了夏天的时候,以下两个条件至少满足一个:
1.对于任意1<=j<=i-1,Hj<=Hi或IOI草j已经被拔除
2.对于任意i+1<=j<=N,Hj<=Hi或IOI草j已经被拔除
用最终收获的果实的总价格减掉拔除IOI草的花销的总和,即为JOI君的收益。那么JOI君能从IOI草中获取的最大利益到底有多少呢?
第一行一个正整数N,表示田地被分为了N个区域。
接下来N行,第i行(1<=i<=N)三个空白分割的正整数Hi,Pi,Ci,表示第i株IOI草在春天时高度会生长至Hi,秋天收获的果实的价格为Pi,拔除所需费用为Ci。
输出一行一个整数,表示JOI君能获得的最大利益
7
22 60 30
46 40 30
36 100 50
11 140 120
38 120 20
24 90 60
53 50 20
320
拔除IOI草2和IOI草7,剩余的IOI草如下图所示:
IOI草1、3、5、6的果实价格分别为60、100、120、90,拔除IOI草2和IOI草7的花销分别为30、20,总收益为320,这是所有方案中的最大值。
比赛时我就已经想到了接近正解的方法,只可惜我太弱,没有把它A掉。
一眼看上去就知道是个 d p dp dp。
设 l i l_{i} li表示编号按顺序取,最多能取到的价值。
很容易可以想到状态转移方程:
c o s t k cost_{k} costk表示 h k h_{k} hk比 h i h_{i} hi大的植株把它们铲除需要的代价。
不过这是一个 O ( n 2 ) O(n^{2}) O(n2)的 d p dp dp,当然不可以过,所以我们要考虑优化。
考虑线段树,先把所有高度离散化,然后每次查询前面最大的答案。维护的时候就是将线段树中比它小或等于的部分的值全部减去 c o s t i cost_{i} costi,然后将当前草的 l i l_{i} li插入线段树的 h i h_{i} hi处。也就是说,我们每一次提前把 c o s t i cost_{i} costi减掉。
#include
#include
#include
#include
#define ll long long
using namespace std;
const int N=1e5+5;
const ll INF=1e17;
int n,cnt=0;
int a[N],b[N],c[N],h[N];
map <int,int> hash;
ll l[N],r[N],lazy[N<<2],tree[N<<2];
ll ans=0;
void down(int w) {
tree[w<<1]+=lazy[w];lazy[w<<1]+=lazy[w];
tree[(w<<1)|1]+=lazy[w];lazy[(w<<1)|1]+=lazy[w];
lazy[w]=0;
}
ll query(int now,int l,int r,int x) {
if(l>x)return -INF;
if(r<=x) {
if(l<r)down(now);
return tree[now];
}
int mid=(l+r)>>1;
down(now);
ll flow=max(query(now<<1,l,mid,x),query((now<<1)|1,mid+1,r,x));
tree[now]=max(tree[now<<1],tree[(now<<1)|1]);
return flow;
}
void update(int now,int l,int r,int x,int y,ll val) {
if(l>y)return;
if(r<x)return;
if(l>=x&&r<=y) {
tree[now]+=val;
lazy[now]+=val;
return;
}
int mid=(l+r)>>1;
down(now);
update(now<<1,l,mid,x,y,val);
update((now<<1)|1,mid+1,r,x,y,val);
tree[now]=max(tree[now<<1],tree[(now<<1)|1]);
}
void modify(int now,int l,int r,int x,ll val) {
if(l>x)return;
if(r<x)return;
if(l==r) {
tree[now]=val;
lazy[now]=0;
return;
}
int mid=(l+r)>>1;
down(now);
modify(now<<1,l,mid,x,val);
modify((now<<1)|1,mid+1,r,x,val);
tree[now]=max(tree[now<<1],tree[(now<<1)|1]);
}
int main() {
freopen("herbary.in","r",stdin);
freopen("herbary.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d%d%d",&a[i],&b[i],&c[i]);
h[i]=a[i];
}
sort(h+1,h+1+n);
for(int i=1;i<=n;i++)
if(!hash[h[i]])hash[h[i]]=++cnt;
for(int i=1;i<=n;i++) {
a[i]=hash[a[i]];
ll q=query(1,0,cnt,a[i]);
l[i]=q+b[i];
update(1,0,cnt,0,a[i]-1,-c[i]);
modify(1,0,cnt,a[i],l[i]);
}
memset(tree,0,sizeof(tree));
memset(lazy,0,sizeof(lazy));
for(int i=n;i;i--) {
ll q=query(1,0,cnt,a[i]);
r[i]=q+b[i];
update(1,0,cnt,0,a[i]-1,-c[i]);
modify(1,0,cnt,a[i],r[i]);
}
for(int i=1;i<=n;i++)
if(ans<l[i]+r[i]-b[i])ans=l[i]+r[i]-b[i];
printf("%lld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}