D. Optimal Partition_线段树优化DP

D. Optimal Partition

题目大意:

给一个数组a,现在要将其分成若干个子段。

若该段和大于0则价值加上段长度;

若该段和等于0则价值不变;

若该段和小于0则价值减去段长度;

问最大价值。

思路和代码:

赛时这题没什么思路,甚至没想到dp...

看到呆瓜队友交了几发居然在贪...

首先这是一个dp优化,很对我胃口~

不难想到O(n2)的dp:(其中p为前缀和)

稍做整理后可以得到:

于是我们可以将p作为线段树的“x轴”,维护{dpj-j,dpj,dpj+j}三棵线段树。这样的话就可以将复杂度降低到O(nlogn)。

当然,p数组范围太大,不可能直接作为线段树的叶子节点,所以我们可以先做一次离散化

ll p[N] ;

struct Node{
	int l , r ;
	ll val ;
}tr[N << 2][3];
// 0 1 2
// + 0 -

void build(int now , int l , int r){// cout << l << " " << r << "\n" ;
	tr[now][k] = {l , r , -INF} ;
	if(l == r){
		tr[now][k].val = -INF ; return ;
	}
	int mid = l + r >> 1 ;
	build(now << 1 , l , mid) ;
	build(now << 1 | 1 , mid + 1 , r) ;
	tr[now][k].val = max(tr[now << 1][k].val , tr[now << 1 | 1][k].val) ;
}

ll query(int now , int l , int r){
	if(l <= tr[now][k].l && tr[now][k].r <= r) return tr[now][k].val ;
	int mid = tr[now][k].l + tr[now][k].r >> 1 ;
	ll res = -INF ;
	if(l <= mid) res = max(res , query(now << 1 , l , r)) ;
	if(mid < r ) res = max(res , query(now << 1 | 1 , l , r)) ;
	return res ;
}

void modify(int now , int x , ll data){
	if(tr[now][k].l == tr[now][k].r) {
		tr[now][k].val = max(tr[now][k].val , data) ;
		return ;
	}
	int mid = tr[now][k].l + tr[now][k].r >> 1 ;
	if(x <= mid) modify(now << 1 , x , data) ;
	else modify(now << 1 | 1 , x , data) ;
	tr[now][k].val = max(tr[now << 1][k].val , tr[now << 1 | 1][k].val) ;
}

void solve(){
	cin >> n ;
	rep(i , 1 , n){
		ll tmp ;
		cin >> tmp ;
		p[i] = p[i - 1] + tmp ;
	}
	
	vct<ll> a ;
	a.pb(0) ;
	rep(i , 1 , n) a.pb(p[i]) ;
	
	sort(a.begin() , a.end()) ;
	a.erase(unique(a.begin() , a.end()) , a.end()) ;
	
	vct<int> idx(n + 1 , 0) ;
	rep(i , 0 , n)
		idx[i] = lower_bound(a.begin() , a.end() , p[i]) - a.begin() + 1 ;

	rep(i , 1 , 3){
		k = i - 1 ;
		build(1 , 1 , n + 1) ;
		modify(1 , idx[0] , 0) ;
	}
	
	vct<ll> dp(n + 1 , -INF) ;

	for(ll i = 1 ; i <= n ; i ++ ){
		ll now = idx[i] ;
		k = 0 ;
		if(now > 1)
		dp[i] = max(dp[i] , query(1 , 1 , now - 1) + i) ;
		
		k = 1 ;
		dp[i] = max(dp[i] , query(1 , now , now)) ;
		
		k = 2 ;
		if(now + 1 <= n + 1) 
		dp[i] = max(dp[i] , query(1 , now + 1 , n + 1) - i) ;
		
		k = 0 ;
		modify(1 , now , dp[i] - i) ;
		
		k = 1 ;
		modify(1 , now , dp[i]) ;
		
		k = 2 ;
		modify(1 , now , dp[i] + i) ;
		
	}
	cout << dp[n] << "\n" ;
	
}//code_by_tyrii 
小结:

不太懂为什么要特别把0加入离散化中(虽然我知道这样确实是对的)

这题有点离谱哦,不会做先不说,做出来的也差别好多。

用线段树的和用树状数组的就差好多好多(毕竟区间查询单点修改)

研究一下树状数组做法再水一篇咯~

posted @   tyrii  阅读(148)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示