您的位置:首页 > 其它

[JZOJ4296]有趣的有趣的家庭菜园

2016-04-08 20:58 337 查看

题目描述

有n棵植株排成一排,第i棵植株的高度、价值和拔除的花费分别为hi、pi和ci。

能够获得第i棵植株的价值,当且仅当这棵植株左边所有高度大于它的植株都被拔除,或者右边所有高度大于它的植株都被拔除。

最大化收益(价值和与花费和的差)。

3≤n≤105,1≤hi,pi,ci≤109

题目分析

我们观察获得价值的条件,按照x轴为编号,y轴为高度,显然最终贡献价值的植株构成的图像就是一个开口朝下的单峰函数(可以特殊化成一条单调不上升/不下降的曲线)。

我们枚举函数最高点,然后求两边答案最大值之和即可。两边答案求法类似,我们只讨论从左到右。

设fi表示选择第i棵植株能造成的最大收益,先列出最暴力的dp方程:

fi=max{fj−∑j<k<i且hk>hick|j<i且hj≤hi}

这样我们就能弄出O(n2)的算法。

考虑使用数据结构优化。可以发现,一个植株,对于后面的所有比它矮的植株的影响,就是−ci,对于所有不低于它的植株,都可以转移答案。

因此我们以离散化的高度为下标,使用线段树某高度的答案。收取一个点的答案之后就对比它小的下标执行区间加−ci操作,对不小于它的执行对fi取max操作。

现在我们要按照时间顺序维护两种操作:区间加、区间max,这样怎么做呢?。

将一个点x的标记记为max(mxx,a)+b

考虑区间加c,显然直接变成max(mxx,a)+(b+c)

考虑区间对c取max,推算一下max(max(mxx,a)+b,c)=max(max(mxx,a),c−b)+b

下传标记给儿子节点时先要下传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
;

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
,p
,c
,lab
;
LL f[2]
;
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]
,f[1][1]),ans);
printf("%lld\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: