题目描述
有
n
棵植株排成一排,第
能够获得第
i
棵植株的价值,当且仅当这棵植株左边所有高度大于它的植株都被拔除,或者右边所有高度大于它的植株都被拔除。
最大化收益(价值和与花费和的差)。
题目分析
我们观察获得价值的条件,按照
x
轴为编号,
我们枚举函数最高点,然后求两边答案最大值之和即可。两边答案求法类似,我们只讨论从左到右。
设
fi
表示选择第
i
棵植株能造成的最大收益,先列出最暴力的
fi=max{fj−∑j<k<i且hk>hick|j<i且hj≤hi}
这样我们就能弄出 O(n2) 的算法。
考虑使用数据结构优化。可以发现,一个植株,对于后面的所有比它矮的植株的影响,就是 −ci ,对于所有不低于它的植株,都可以转移答案。
因此我们以离散化的高度为下标,使用线段树某高度的答案。收取一个点的答案之后就对比它小的下标执行区间加 −ci 操作,对不小于它的执行对 fi 取 max 操作。
现在我们要按照时间顺序维护两种操作:区间加、区间 max ,这样怎么做呢?。
将一个点 x 的标记记为
考虑区间加 c ,显然直接变成
考虑区间对 c 取
下传标记给儿子节点时先要下传 max 标记。还有注意 max 标记是否存在要使用布尔数组什么的标注一下,不然可能会和初值乱搞(如果你们维护得很好就自动无视这句话)。
然后就是时间复杂度为 O(nlog2n) 的算法。
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
using namespace std;
typedef long long LL;
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-'0';
ch=getchar();
}
return x*f;
}
const int N=100500;
struct D
{
int v,id;
}s[N];
bool operator<(D a,D b)
{
return a.v<b.v;
}
struct segment_tree
{
LL tag[N<<2][2],mx[N<<2];//0:add;1:max
bool tg[N<<2];
void init()
{
memset(tag,0,sizeof tag);
memset(tg,0,sizeof tg);
memset(mx,0,sizeof mx);
}
void merge(int x,LL y,bool tp)
{
if (!tp)
tag[x][0]+=y;
else
{
if (tg[x])
tag[x][1]=max(tag[x][1],y-tag[x][0]);
else
tag[x][1]=y-tag[x][0];
tg[x]=true;
}
}
void mark(int x,LL y,bool tp)
{
merge(x,y,tp);
if (!tp)
mx[x]+=y;
else
mx[x]=max(mx[x],y);
}
void clear(int x,int l,int r)
{
if (l==r)
return;
if (tag[x][1])
{
mark(x<<1,tag[x][1],1),mark(x<<1|1,tag[x][1],1);
tag[x][1]=0;
tg[x]=false;
}
if (tag[x][0])
{
mark(x<<1,tag[x][0],0),mark(x<<1|1,tag[x][0],0);
tag[x][0]=0;
}
}
void update(int x)
{
mx[x]=max(mx[x<<1],mx[x<<1|1]);
}
LL query(int x,int y,int l,int r)
{
clear(x,l,r);
if (l==r)
return mx[x];
int mid=l+r>>1;
if (y<=mid)
return query(x<<1,y,l,mid);
else
return query(x<<1|1,y,mid+1,r);
}
void change(int x,int st,int en,int l,int r,bool tp,LL edit)
{
clear(x,l,r);
if (st==l&&en==r)
{
mark(x,edit,tp);
return;
}
int mid=l+r>>1;
if (en<=mid)
change(x<<1,st,en,l,mid,tp,edit);
else
if (mid+1<=st)
change(x<<1|1,st,en,mid+1,r,tp,edit);
else
change(x<<1,st,mid,l,mid,tp,edit),change(x<<1|1,mid+1,en,mid+1,r,tp,edit);
update(x);
}
}t;
int h[N],p[N],c[N],lab[N];
LL f[2][N];
int n,ind;
LL ans;
int main()
{
freopen("herbary.in","r",stdin);
freopen("herbary.out","w",stdout);
n=read();
for (int i=1;i<=n;i++)
h[i]=read(),p[i]=read(),c[i]=read();
for (int i=1;i<=n;i++)
s[i].v=h[i],s[i].id=i;
sort(s+1,s+1+n);
ind=0;
for (int i=1;i<=n;i++)
lab[s[i].id]=(s[i].v==s[i-1].v)?ind:++ind;
t.init();
for (int i=1;i<=n;i++)
{
LL get=t.query(1,lab[i],1,ind);
f[0][i]=get+p[i];
if (lab[i]>1)
t.change(1,1,lab[i]-1,1,ind,0,-c[i]);
t.change(1,lab[i],ind,1,ind,1,f[0][i]);
}
for (int i=2;i<=n;i++)
f[0][i]=max(f[0][i],f[0][i-1]);
t.init();
for (int i=n;i>=1;i--)
{
LL get=t.query(1,lab[i],1,ind);
f[1][i]=get+p[i];
if (lab[i]>1)
t.change(1,1,lab[i]-1,1,ind,0,-c[i]);
t.change(1,lab[i],ind,1,ind,1,f[1][i]);
}
for (int i=n-1;i>=1;i--)
f[1][i]=max(f[1][i],f[1][i+1]);
for (int i=1;i<n;i++)
ans=max(f[0][i]+f[1][i+1],ans);
ans=max(max(f[0][n],f[1][1]),ans);
printf("%lld\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}