线段树优化DP学习笔记 & JZOJ 孤独一生题解
在 的世界里
有一种题需要单调队列优化
一般在此时, 和它的决策集合 在转移时 不和 粘在一起(即所有的 转移到 时 关于 的部分全都与 无关),
果真如此,我们就可以用单调队列优化,留下可能用到的更有决策
而很多情况下 有联系, 对于不同的 ,转移计入的贡献和 都有联系,如
此时单调队列就无用武之地了
那怎么办
各种奇妙的优化
本文我们来学习线段树优化 ,解决上面问题
在例题中感受美
JZOJ孤独一生
题目大意:
将序列 划分为两个可空集合。
对于一个集合 ,其中要求 ,它的花费是
最小化花费
解法:
还好的
设 表示处理完 个元素的最小花费
那么转移考虑当前的 所属集合前一个元素是谁
枚举一个 , 和 同属一个集合
那么转移就是
其中
一眼望去
再望,绝对值太糟糕(单调队列挂了花)
怎办?
去!
用线段树维护
······
不会啊?
如何用线段树
君不见,绝对值从天上来,纠缠 不可休······
插!
分类讨论,绝对值分开,把式子变好看,这样 就分开了
若 则
整理得
若 则
整理得
总算把 分开了
此时做商量
如何让 式最小,因为决定于 ,所以让后面一堆最小
发现可以用线段树维护 的最小值
因为判定时和 有关,所以维护以 为下标的权值线段树
具体来说就是在 的位置插入值
因为顺序枚举,所以算完一个插一个,保证正确性
只需取出线段树中 的最小值即可
式同理(再开一棵权值线段树,因为另一种 的贡献长得不一样)
代码(常数巨大,不得不开O)
#pragma GCC optimize(2) #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N = 500000; int n , h[N + 5] , id[N + 5]; LL f[N + 5] , s[N + 5]; inline LL Min(LL x , LL y) { return x < y ? x : y; } inline int Abs(int x) { return x < 0 ? -x : x; } struct node{ int l , r; }e[N + 5]; struct tree{ LL tr[(N << 2) + 5]; inline void full() { memset(tr , 120 , sizeof(tr)); } inline void change(int k , int l , int r , int x , LL v) { tr[k] = Min(tr[k] , v); if (l == r) return; register int mid = (l + r) >> 1; if (x <= mid) change(k << 1 , l , mid , x , v); else change(k << 1 | 1 , mid + 1 , r , x , v); } inline LL query(int k , int l , int r , int x , int y) { if (l >= x && r <= y) return tr[k]; register int mid = (l + r) >> 1; register LL res = 1e18; if (x <= mid) res = Min(res , query(k << 1 , l , mid , x , y)); if (y > mid) res = Min(res , query(k << 1 | 1 , mid + 1 , r , x , y)); return res; } }p,q; inline int read() { register char ch = getchar(); register LL res = 0; while (ch < '0' || ch > '9') ch = getchar(); while (ch >= '0' && ch <= '9') res = res * 10 + ch - '0' , ch = getchar(); return (int)res; } inline bool cmp(node x , node y) { return x.l < y.l; } int main() { // freopen("a.in" , "r" , stdin); n = read(); for(register int i = 1; i <= n; i++) { h[i] = read() , e[i].l = h[i] , e[i].r = i; s[i] = s[i - 1] + (LL)Abs(h[i] - h[i - 1]); } sort(e + 1 , e + n + 1 , cmp); for(register int i = 1; i <= n; i++) id[e[i].r] = i; q.full() , p.full(); q.change(1 , 0 , n , 0 , 0); p.change(1 , 0 , n , 0 , 0); f[0] = 0; for(register int i = 1; i <= n; i++) { LL x = q.query(1 , 0 , n , 0 , id[i]); LL y = p.query(1 , 0 , n , id[i] , n); f[i] = Min(s[i - 1] + h[i] + x , s[i - 1] - h[i] + y); q.change(1 , 0 , n , id[i - 1] , f[i] - s[i] - h[i - 1]); p.change(1 , 0 , n , id[i - 1] , f[i] - s[i] + h[i - 1]); } for(register int i = 1; i <= n; i++) f[n] = Min(f[n] , f[i] + s[n] - s[i]); printf("%lld" , f[n]); }
很多时候,线段树优化 的具体方法因题而异,不可一概而论
要想更好的掌握,就要多做题
标签:
线段树
, 动态规划_数据结构优化
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具