职业经营家庭菜园的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草中获取的最大利益到底有多少呢?
这题就是要找一个上凸的一个序列,这个明显可以用DP来做。
设f[i]为i在这个序列上,前面选进来的数都不比当前这个数大。
那么DP方程很明显f[i]=max(f[j]+b[i]-cost,f[i]),cost为i到j中间所有比h[i]大的数拔掉胡费用和(因为枚举的j为在这个序列中i的上一个数)。
然后再设一个g[i]倒着做一遍,最后的答案为f[i]+g[i]-b[i]的最大值。
其实DP是可以打成O( n2 )的。
因为必须是h[j]<=h[i]才可以转移,cost随着j越大而越小。
那么我们的j就倒着枚举,然后每当h[j]>h[i]时,把cost加上拔掉j这个草的值,原因很显然。
首先很明显要做一个离散化。
这个要取一个最大值,明显可以用线段树优化。
因为对于当前这个f[i]的转移,如果要从j转移过来,那么f[j..i]一定要减去中间那些比h[j]和h[i]大的数一定要减去他们的拔草值才能转移。
那么我们每一次把f[i]处理完之后,把比h[i]小的h[k],全部减去i的拔草值,这里用线段树区间减。
然后把i+1到末尾的所有f[l]都对f[i]取一个最大值,这里用线段树做。
注意,线段树最大值和加减的标记下传的过程中要注意,如果先加减之后,然后取了最大值,不能对最大值的标记直接进行加减,再打一个特判的标记就好了。
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
typedef long long ll;
using namespace std;
const int maxn=100007;
int i,j,k,l,n,m;
ll tt,ans;
ll a[maxn],b[maxn],c[maxn],d[maxn],tot;
ll f[maxn],g[maxn];
struct nod{
ll a,d;
}e[maxn];
struct node{
ll da,biao,biao1,biao2;
}t[maxn*10];
bool cmp(nod x,nod y){
return x.a<y.a;
}
void doing1(int x,ll y){
t[x].da=max(t[x].da,y);
ll u=y-t[x].biao;
if(t[x].biao2)t[x].biao1=max(t[x].biao1,u);
else t[x].biao2=1,t[x].biao1=u;
}
void doing2(int x,ll y){
t[x].da+=y;
t[x].biao+=y;
}
void doing(int x){
if(t[x].biao2!=0){
doing1(x*2,t[x].biao1);
doing1(x*2+1,t[x].biao1);
t[x].biao1=t[x].biao2=0;
}
if(t[x].biao!=0){
doing2(x*2,t[x].biao);
doing2(x*2+1,t[x].biao);
t[x].biao=0;
}
}
void change(int x,int l,int r,int y,int z,ll o) {
if (y>z) return;
if (l==y&&r==z) {doing2(x,o);return;}
int mid=(l+r)/2;
doing(x);
if (z<=mid) change(x*2,l,mid,y,z,o);
else if (y>mid) change(x*2+1,mid+1,r,y,z,o);
else change(x*2,l,mid,y,mid,o),change(x*2+1,mid+1,r,mid+1,z,o);
t[x].da=max(t[x*2].da,t[x*2+1].da);
}
void change1(int x,int l,int r,int y,int z,ll o) {
if (y>z) return;
if (l==y&&r==z) {doing1(x,o);return;}
int mid=(l+r)/2;
doing(x);
if (z<=mid) change1(x*2,l,mid,y,z,o);
else if (y>mid) change1(x*2+1,mid+1,r,y,z,o);
else change1(x*2,l,mid,y,mid,o),change1(x*2+1,mid+1,r,mid+1,z,o);
t[x].da=max(t[x*2].da,t[x*2+1].da);
}
ll find(int x,int l,int r,int y){
if (l==r) return t[x].da;
int mid=(l+r)/2;
doing(x);
if (y<=mid)return find(x*2,l,mid,y);
else return find(x*2+1,mid+1,r,y);
}
int main(){
freopen("herbary.in","r",stdin);
freopen("herbary.out","w",stdout);
scanf("%d",&n);
fo(i,1,n){
scanf("%lld%lld%lld",&e[i].a,&b[i],&c[i]);
e[i].d=i;
}
sort(e+1,e+n+1,cmp);
tot=1;a[e[1].d]=tot;
fo(i,2,n){
if(e[i-1].a!=e[i].a)a[e[i].d]=++tot;
else a[e[i].d]=tot;
}
fo(i,1,n){
f[i]=b[i]+find(1,1,tot,a[i]);
change(1,1,tot,1,a[i]-1,-c[i]);
change1(1,1,tot,a[i],tot,f[i]);
}
memset(t,0,sizeof(t));
fod(i,n,1){
g[i]=b[i]+find(1,1,tot,a[i]);
change(1,1,tot,1,a[i]-1,-c[i]);
change1(1,1,tot,a[i],tot,g[i]);
}
fo(i,1,n){
ans=max(ans,f[i]+g[i]-b[i]);
}
printf("%lld",ans);
}