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加入离散化中(虽然我知道这样确实是对的)
这题有点离谱哦,不会做先不说,做出来的也差别好多。
用线段树的和用树状数组的就差好多好多(毕竟区间查询单点修改)
研究一下树状数组做法再水一篇咯~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具