JOI 有趣的有趣的家庭菜园Fgarden

【题目描述】


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


【输入格式】


第一行一个正整数N,表示田地被分为了N个区域。

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


【输出格式】



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


【样例输入】

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

【样例输出】

320

【提示】

拔除IOI草2和IOI草7,剩余的IOI草如下图所示:

IOI草1、3、5、6的果实价格分别为60、100、120、90,拔除IOI草2和IOI草7的花销分别为30、20,总收益为320,这是所有方案中的最大值。



对于30%的数据,N<=20

对于45%的数据,N<=300

对于60%的数据,N<=5000

对于100%的数据:

3<=N<=10^5

1<=Hi<=10^9 (1<=i<=N)

1<=Pi<=10^9 (1<=i<=N)

1<=Ci<=10^9 (1<=i<=N)

最终有贡献的草一定是单调上升再单调下降

那么方程f[i]=max(f[j]+v[i]);h[j]<=h[i] j<=i

g[i]=max(g[j]+v[i]);h[j]<=h[i] j>=i

答案取f[i]+g[i]-v[i]

将高度离散化 用线段树优化转移

考虑一颗草种进去 他会对后面的比他矮的草有影响(需要拔掉这颗)那么就对线段树[1,h[i]]这个区间减上cost[i] 

时间复杂度O(nlogn)

#include
#include
#include
using namespace std;
const int maxn=200000+10;
typedef long long LL;
LL f[maxn],g[maxn];
#define INF 1<<64-1
#define lc rt<<1
#define rc rt<<1|1
LL maxx[maxn<<2],tag[maxn<<2];
LL h[maxn],v[maxn],c[maxn],B[maxn],hash[maxn];
int tot;
inline void pushdown(int o){
	if(tag[o]!=0){
		maxx[o<<1]+=tag[o]; 
		maxx[o<<1|1]+=tag[o];
		tag[o<<1]+=tag[o]; 
		tag[o<<1|1]+=tag[o];
		tag[o]=0;
	}
}
inline int bs(LL x){
	int l=1,r=tot;
	while(l+1>1;
		if(hash[mid]>=x)
			r=mid;
		else l=mid+1;
	}
	if(hash[l]==x)
		return l;
	return r;
}
inline LL qmax(int o,int l,int r,int L,int R){
	if(L<=l&&r<=R)
		return maxx[o];
	int mid=(l+r)>>1; 
	LL ans=0;
	int vis=0;
	pushdown(o);
	if(L<=mid){
		if(!vis){
			ans=qmax(o<<1,l,mid,L,R);
			vis=1;
		}
		else ans=max(ans,qmax(o<<1,l,mid,L,R));
	}
	if(R>mid){
		if(!vis){
			ans=qmax(o<<1|1,mid+1,r,L,R);
			vis=1;
		}
		else ans=max(ans,qmax(o<<1|1,mid+1,r,L,R));
	}
	return ans;
}
inline void add(int o,int l,int r,int L,int R,LL x){
	if(L<=l&&r<=R){ 
		maxx[o]+=x; 
		tag[o]+=x; 
		return; 
	}
	int mid=(l+r)>>1;
	pushdown(o);
	if(L<=mid)
		add(o<<1,l,mid,L,R,x);
	if(R>mid)
		add(o<<1|1,mid+1,r,L,R,x);
	maxx[o]=max(maxx[o<<1],maxx[o<<1|1]);
}
inline void change(int o,int l,int r,int x,LL y){
	if(l==r&&l==x){ 
		maxx[o]=max(maxx[o],y); 
		return; 
	}
	int mid=(l+r)>>1;
	pushdown(o);
	if(x<=mid)
		change(o<<1,l,mid,x,y);
	else change(o<<1|1,mid+1,r,x,y);
	maxx[o]=max(maxx[o<<1],maxx[o<<1|1]);
}
int main(){
	freopen("Fgarden.in","r",stdin);
	freopen("Fgarden.out","w",stdout);
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){ 
		scanf("%lld %lld %lld",&h[i],&v[i],&c[i]);
		B[i]=h[i]; 
	}
	sort(B+1,B+n+1);
	hash[++tot]=B[1];
	for(int i=2;i<=n;i++)
		if(B[i]!=B[i-1])
			hash[++tot]=B[i];
	memset(maxx,0,sizeof(maxx));
	memset(tag,0,sizeof(tag));
	for(int i=1;i<=n;i++){
		int u=bs(h[i]);
		f[i]=v[i]+qmax(1,1,tot,1,u);
		add(1,1,tot,1,u,-c[i]);
		change(1,1,tot,u,f[i]);
	}
	memset(maxx,0,sizeof(maxx));
	memset(tag,0,sizeof(tag));
	for(int i=n;i>=1;i--){
		int u=bs(h[i]);
		g[i]=v[i]+qmax(1,1,tot,1, u);
		add(1,1,tot,1,u,-c[i]);
		change(1,1,tot,u,g[i]);
	}
	LL ans=0;
	for(int i=1;i<=n;i++)
		ans=max(ans,f[i]+g[i]-v[i]);
	printf("%lld\n",ans);
	/*for(int i=1;i<=n;i++)
		printf("%lld\n",f[i]);*/
	//for(int i=1;i<=n;i++)
	//	printf("%lld\n",g[i]);
return 0;
}


 

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