【NOIP2015模拟11.2】有趣的有趣的家庭菜园

Description

职业经营家庭菜园的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草中获取的最大利益到底有多少呢?

Input

第一行一个正整数N,表示田地被分为了N个区域。
接下来N行,第i行(1<=i<=N)三个空白分割的正整数Hi,Pi,Ci,表示第i株IOI草在春天时高度会生长至Hi,秋天收获的果实的价格为Pi,拔除所需费用为Ci。

Output

输出一行一个整数,表示JOI君能获得的最大利益

Sample Input

7
22 60 30
46 40 30
36 100 50
11 140 120
38 120 20
24 90 60
53 50 20

Sample Output

320

Hint

拔除IOI草2和IOI草7,剩余的IOI草如下图所示:
在这里插入图片描述
IOI草1、3、5、6的果实价格分别为60、100、120、90,拔除IOI草2和IOI草7的花销分别为30、20,总收益为320,这是所有方案中的最大值。

Solution

比赛时我就已经想到了接近正解的方法,只可惜我太弱,没有把它A掉。
一眼看上去就知道是个 d p dp dp
l i l_{i} li表示编号按顺序取,最多能取到的价值。
很容易可以想到状态转移方程:

l i = m a x ( l i , l j − ∑ c o s t k ) l_{i}=max(l_{i},l_{j}-\sum cost_{k}) li=max(li,ljcostk)

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减掉。

Code

#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;
}

你可能感兴趣的:(动态规划,线段树)