[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; }
相关文章推荐
- <C#入门经典>学习笔记之结构、数组及字符串
- The 11th Zhejiang Provincial Collegiate Programming Contest
- SPF(poj 1523) 割点入门
- opencv 模版匹配matchtemplate方法介绍
- css 复习-position定位
- mysql设置远程访问数据库的多种方法
- RabbitMQ 安装和配置
- 2016年中国虚拟现实行业研究报告
- 【脚本化文档】——DOM概览&选取文档元素
- PHP文件上传的实现及其介绍
- 进程与线程大战几百回合?
- 本周耐撕团队个人总结
- 【复习笔记】 cocos2d-x 2.x 渲染特效实现 六 节点树的描边效果
- 工作室日记——QG面试
- Boostrap框架学习总结
- HDU 1069 Monkey and Banana 基础DP
- 使用CocoaPods源代码管理器
- java mail使用qq邮箱发邮件的配置方法
- 九度 1007
- python爬虫(爬取蜂鸟网高像素图片)_空网页,错误处理