题解:[JOISC 2015] 有趣的家庭菜园 2 / Growing Vegetables is Fun 2

题目传送门

题意分析

记原题面中 \(n,H_i,P_i,C_i\) 分别为 \(n,h_i,p_i,c_i\)

考虑 DP 求解最大收益,不难发现最终有效的 \(h_i\) 一定是先上升再下降的。

对于这种「单峰」的东西,DP 可以考虑做两次:上升和下降,即从前往后 \(h_i\) 不降和从前往后 \(h_i\) 不增。

\(h_i\) 不降为例,设 \(\textit{dp}_{i,j}\) 表示 \(1\sim i\),有效的 \(h_i\) 最大值为 \(j\) 的时候的最大收益。

\(h\) 显然可以离散化。

考虑:

  • \(h_i\) 有贡献,那么:

    \[\textit{dp}_{i,h_i}=\max_{j=1}^{h_i}\textit{dp}_{i-1,j}+p_i \]

  • \(h_i\) 没有贡献。因为收益最大,因此被挡住的时候不应该拔掉:

    • \(h_i\) 被挡住,即 \(h_i<j\),有 \(\textit{dp}_{i,j}=\textit{dp}_{i-1,j}\)
    • \(h_i\) 被拔掉,即 \(j<h_i\),有 \(\textit{dp}_{i,j}=\textit{dp}_{i-1,j}-c_i\)

可以发现这是一个 \(\mathcal O\left(n^2\right)\) 的 DP,可以得到 \(\text{30pts}\)

又可以发现 \(i\) 这一维的信息仅与 \(i-1\) 有关,这一维可以直接砍掉。

如何优化呢?DP 式中很多修改都是相同的,即对于 \(j<h_i\),减去 \(c_i\);对于 \(j=h_i\),前缀 \(\max\) 维护;对于 \(j>h_i\) 不变。

容易想到线段树优化 DP,区间减去 \(c_i\)、求前缀 \(\max\)、单点修改即可。

时间复杂度 \(\mathcal O(n\log n)\)

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
#define int long long
constexpr const int N=1e5;
int n,m,h[N+1],p[N+1],c[N+1];
int pre[N+1],suf[N+1];
void discrete(){
	static int tmp[N+1];
	for(int i=1;i<=n;i++){
		tmp[i]=h[i];
	}
	sort(tmp+1,tmp+n+1);
	m=unique(tmp+1,tmp+n+1)-tmp-1;
	for(int i=1;i<=n;i++){
		h[i]=lower_bound(tmp+1,tmp+m+1,h[i])-tmp;
	}
}
struct segTree{
	struct node{
		int l,r;
		int max,tag;
		
		int size(){
			return r-l+1;
		}
	}t[N<<2|1];
	
	void up(int p){
		t[p].max=::max(t[p<<1].max,t[p<<1|1].max);
	}
	void build(int p,int l,int r){
		t[p]={l,r};
		if(l==r){
			return;
		}
		int mid=l+r>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		up(p);
	}
	void down(int p){
		if(t[p].tag){
			t[p<<1].max+=t[p].tag;
			t[p<<1].tag+=t[p].tag;
			t[p<<1|1].max+=t[p].tag;
			t[p<<1|1].tag+=t[p].tag;
			t[p].tag=0;
		}
	}
	void add(int p,int l,int r,int x){
		if(r<l){
			return;
		}
		if(l<=t[p].l&&t[p].r<=r){
			t[p].max+=x;
			t[p].tag+=x;
			return;
		}
		down(p);
		if(l<=t[p<<1].r){
			add(p<<1,l,r,x);
		}
		if(t[p<<1|1].l<=r){
			add(p<<1|1,l,r,x);
		}
		up(p);
	}
	void chkmax(int p,int x,int k){
		if(t[p].l==t[p].r){
			t[p].max=::max(t[p].max,k);
			return;
		}
		down(p);
		if(x<=t[p<<1].r){
			chkmax(p<<1,x,k);
		}else{
			chkmax(p<<1|1,x,k);
		}
		up(p);
	}
	int max(int p,int l,int r){
		if(l<=t[p].l&&t[p].r<=r){
			return t[p].max;
		}
		down(p);
		int ans=-2147483647;
		if(l<=t[p<<1].r){
			ans=max(p<<1,l,r);
		}
		if(t[p<<1|1].l<=r){
			ans=::max(ans,max(p<<1|1,l,r));
		}
		return ans;
	}
}t;
main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>h[i]>>p[i]>>c[i];
	}
	discrete();
	t.build(1,1,m);
	for(int i=1;i<=n;i++){
		pre[i]=t.max(1,1,h[i])+p[i];
		t.add(1,1,h[i]-1,-c[i]);
		t.chkmax(1,h[i],pre[i]);
	}
	t.build(1,1,m);
	for(int i=n;1<=i;i--){
		suf[i]=t.max(1,1,h[i])+p[i];
		t.add(1,1,h[i]-1,-c[i]);
		t.chkmax(1,h[i],suf[i]);
	}
	int ans=pre[1]+suf[1]-p[1];
	for(int i=2;i<=n;i++){
		ans=max(ans,pre[i]+suf[i]-p[i]);
	}
	cout<<ans<<'\n';
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2026-02-10 15:22  TH911  阅读(19)  评论(0)    收藏  举报