在 \(DP\) 的世界里

有一种题需要单调队列优化 \(DP\)

一般在此时,\(f_i\) 和它的决策集合 \(f_j\) 在转移时 \(i\) 不和 \(j\) 粘在一起(即所有的 \(j\) 转移到 \(i\)时 关于\(j\) 的部分全都与 \(i\)无关),

果真如此,我们就可以用单调队列优化,留下可能用到的更有决策

而很多情况下 \(i,j\) 有联系,\(i\) 对于不同的 \(j\) ,转移计入的贡献和 \(i,j\) 都有联系,如

\(f_i = \min_{j=1}^{j<i} f_j+s_{i-1}-s_j+|h_i - h_{j-1}|\)

此时单调队列就无用武之地了

那怎么办

各种奇妙的优化

本文我们来学习线段树优化 \(DP\),解决上面问题

在例题中感受美

JZOJ孤独一生

题目大意:

将序列 \(H\) 划分为两个可空集合。

对于一个集合 \(S={P1,P2,...P|S|}\),其中要求 \(P1<P2<...<P|S|\),它的花费是 \(\sum_{i=1}^{|S|} |H_{P_i}-H_{P_{i-1}}|\)

最小化花费

解法:

还好的 \(dp\)

设 \(f_i\) 表示处理完 \(1--i\) 个元素的最小花费

那么转移考虑当前的 \(i\) 所属集合前一个元素是谁

枚举一个 \(j\), \(j-1\) 和 \(i\) 同属一个集合

那么转移就是 \(f_i = \min_{j=1}^{j<i} f_j+s_{i-1}-s_j+|h_i - h_{j-1}|\)

其中 \(s_i = s_{i-1} + |h_i - h_{i-1}|\)

一眼望去 \(O(n^2)\)

再望,绝对值太糟糕(单调队列挂了花)

怎办?

去!

用线段树维护

······

不会啊?

如何用线段树

君不见,绝对值从天上来,纠缠 \((i,j)\) 不可休······

插!

分类讨论,绝对值分开,把式子变好看,这样 \(i,j\) 就分开了

\(1\) 若 \(h_i > h_j\) 则 \(f_i = f_j+s_{i-1}-s_j+h_i-h_{j-1}\)

整理得 \(f_i = (s_{i-1}+h_i)+(f_j-s_j-h_{j-1})\)

\(2\) 若 \(h_i > h_j\) 则 \(f_i = f_j+s_{i-1}-s_j-h_i+h_{j-1}\)

整理得 \(f_i = (s_{i-1}-h_i)+(f_j-s_j+h_{j-1})\)

总算把 \((i,j)\) 分开了

此时做商量

如何让 \(1\) 式最小,因为决定于 \(j\),所以让后面一堆最小

发现可以用线段树维护 \(f_j-s_j-h_{j-1}\) 的最小值

因为判定时和 \(j-1\) 有关,所以维护以 \(h_{j-1}\) 为下标的权值线段树

具体来说就是在 \(h_{j-1}\) 的位置插入值 \(f_j-s_j-h_{j-1}\)

因为顺序枚举,所以算完一个插一个,保证正确性

只需取出线段树中 \(0--h_{i}\) 的最小值即可

\(2\) 式同理(再开一棵权值线段树,因为另一种 \(j\) 的贡献长得不一样)

代码(常数巨大,不得不开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]);
}

很多时候,线段树优化 \(DP\) 的具体方法因题而异,不可一概而论

要想更好的掌握,就要多做题

线段树优化DP学习笔记 & JZOJ 孤独一生题解的更多相关文章

  1. Codeforces Round #426 (Div. 2) D 线段树优化dp

    D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...

  2. BZOJ2090: [Poi2010]Monotonicity 2【线段树优化DP】

    BZOJ2090: [Poi2010]Monotonicity 2[线段树优化DP] Description 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k]. ...

  3. [AGC011F] Train Service Planning [线段树优化dp+思维]

    思路 模意义 这题真tm有意思 我上下楼梯了半天做出来的qwq 首先,考虑到每K分钟有一辆车,那么可以把所有的操作都放到模$K$意义下进行 这时,我们只需要考虑两边的两辆车就好了. 定义一些称呼: 上 ...

  4. 【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp

    题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a varian ...

  5. POJ 2376 Cleaning Shifts (线段树优化DP)

    题目大意:给你很多条线段,开头结尾是$[l,r]$,让你覆盖整个区间$[1,T]$,求最少的线段数 题目传送门 线段树优化$DP$裸题.. 先去掉所有能被其他线段包含的线段,这种线段一定不在最优解里 ...

  6. 洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$

    正解:线段树优化$dp$ 解题报告: 传送门$QwQ$ 难受阿,,,本来想做考试题的,我还造了个精妙无比的题面,然后今天讲$dp$的时候被讲到了$kk$ 先考虑暴力$dp$?就设$f_{i,j}$表示 ...

  7. D - The Bakery CodeForces - 834D 线段树优化dp···

    D - The Bakery CodeForces - 834D 这个题目好难啊,我理解了好久,都没有怎么理解好, 这种线段树优化dp,感觉还是很难的. 直接说思路吧,说不清楚就看代码吧. 这个题目转 ...

  8. 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点

    容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...

  9. Codeforces 1603D - Artistic Partition(莫反+线段树优化 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 学 whk 时比较无聊开了道题做做发现是道神题( 介绍一种不太一样的做法,不观察出决策单调性也可以做. 首先一个很 trivial 的 o ...

  10. 2021.12.08 P1848 [USACO12OPEN]Bookshelf G(线段树优化DP)

    2021.12.08 P1848 [USACO12OPEN]Bookshelf G(线段树优化DP) https://www.luogu.com.cn/problem/P1848 题意: 当农夫约翰闲 ...

随机推荐

  1. 一文带你了解 Spring 的@Enablexxx 注解

    layout: post categories: Java title: 一文带你了解 Spring 的@Enablexxx 注解 tagline: by 子悠 tags: - 子悠 前面的文章给大家 ...

  2. labuladong算法笔记总结

    动态规划解题套路框架 学习计划: 最长回文子序列 〇.必读文章 1.数据结构和算法学习指南(学习算法和刷题的框架思维) 了解数据结构的操作和遍历(迭代or递归) 从树刷起,结合框架思维,有利于理解(回 ...

  3. 【面试真题】ThoughtWorks-编程结对技术面试(一面)-2022年2月11日

    一.技术问题 1.Hbase (1)介绍 (2)项目中是否有用到 于:存大量数据(千万),考虑性能,方便进行数据处理,对其进行分析 自己:ADS层和Flink的数据,DWD计算出的的中间层数据存入DW ...

  4. django框架(部分讲解)

    ORM执行SQL语句 有时候ORM的操作效率可能偏低 我们是可以自己编写SQL的 方式1: raw()方法执行原生sql语句 models.User.objects.raw('select * fro ...

  5. C++进阶(map+set容器模拟实现)

    关联式容器 关联式容器也是用来存储数据的,与序列式容器(如vector.list等)不同的是,其里面存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高.今天要介绍 ...

  6. table表格的合并

    如上图,实现单元格合并功能,不多说,直接上代码 一,添加    :span-method="objectSpanMethod" <el-table :key="ti ...

  7. 过滤器 Filter 与 拦截器 Interceptor 的区别

    引言 说起 Filter 与 Interceptor 的区别,相信很多同学第一感觉就是容易.简单! 毕竟开发中这两个组件使用频率较高,用法也较简单.然后真回答起来有答不出个所以然来,场面尴尬,老丢脸了 ...

  8. JavaScript:对象:如何读取、添加、删除对象的属性?

    如何给对象添加属性? 直接对象名.属性名去添加属性 直接对象名[属性名]去添加属性,此时属性名得是字符串类型,可以直接引号,也可以用变量名 如何读取对象的属性值? 这个操作,和添加属性是一样的,即用点 ...

  9. [OpenCV实战]52 在OpenCV中使用颜色直方图

    颜色直方图是一种常见的图像特征,顾名思义颜色直方图就是用来反映图像颜色组成分布的直方图.颜色直方图的横轴表示像素值或像素值范围,纵轴表示该像素值范围内像素点的个数或出现频率.颜色直方图属于计算机视觉中 ...

  10. JSONObject 相关

    /** * 将json转为对应实体类 */ public static Object jsonToJavaObj(String json, Class cs) { return jsonToJavaO ...