基础数据结构选做

二叉堆

一种 不那么像数据结构 的数据结构,二叉堆几乎只能用来维护最大最小值。

在绝大多数情况下,二叉堆可用 STL 中的 priority_queue(优先队列)代替。

洛谷 P2085 最小函数值 & P1631 序列合并

二叉堆常见套路,从若干递增/递减列中取前 k k k 小/大,具体操作方法是将每一列第一个元素插入堆中,每次取出堆顶后插入堆顶所在列的下一个元素。

最小函数值:每一列为 x x x 递增时同一函数的函数值。

序列合并:每一列为 A A A 中某一元素与 B B B 中所有元素的乘积(当然 A A A B B B 反过来也行),注意可能有重复情况。

洛谷 P1168 中位数 & P1801 黑匣子

对顶堆,这个名称极为形象,即一个大根堆和一个小根堆的顶对在一起。用于维护第 k k k 小/大。

以维护第 k k k 小为例,具体做法是将新加入的元素先插入大根堆,然后将大根堆堆顶弹出并插入小根堆直至大根堆内剩 k k k 个元素,此时大根堆堆顶即为第 k k k 小。

求中位数是其经典应用,在每次插入元素后将元素多的堆内元素转入元素少的堆,直至两堆元素数相等或差 1 1 1,此时两堆顶中其中一个或两堆顶平均数即为中位数(取决于元素总数的奇偶性)。

洛谷 P4053 [JSOI2007] 建筑抢修 & P2949 [USACO09OPEN]Work Scheduling G & 某个题

反悔贪心,若当前贪心最优解为局部最优解则回退一步,严格来说并不算贪心(因为能反悔)。

反悔堆,通过堆来维护反悔贪心。

建筑抢修:贪心选择时间限制最早的建筑,若选择后总时间超过时限则反悔,放弃选择过的建筑中时间花费最长的一个。使用反悔堆维护,将选择过的建筑插入到堆中,堆顶为时间花费最长的建筑。

Work Scheduling:与建筑抢修类似,反悔时放弃获利最小的。

某个题:YbtOJ的二叉堆专题里有大量反悔贪心的题,然而我号到期了,从网上找出来了一个:

家庭作业

反悔时选择 a a a 最大的喝奶茶,直至时间充足。

优先队列优化 Dijkstra & 优化 Prim

利用二叉堆 O ( log ⁡ n ) O(\log n) O(logn) 求最值来优化一些东西,比如 Dijkstra 求最短路和 Prim 求最小生成树。

因为 Dijkstra 中需要 O ( n ) O(n) O(n) 枚举从尚未用于更新的点中找到一个最短路径最小的用于更新,所以我们将这样的点放入堆中,从而优化到 O ( log ⁡ n ) O(\log n) O(logn)。这样优化后的 Dijkstra 时间复杂度为 O ( m log ⁡ n ) O(m\log n) O(mlogn),绝对是在 SPFA 可能被卡的情况下的最好选择。

Prim 原理与 Dijkstra 相似,所以优化方式同上。

ST 表

运用倍增解决 RMQ 问题,预处理 O ( n log ⁡ n ) O(n\log n) O(nlogn),查询 O ( 1 ) O(1) O(1),但不支持修改。

事实上,ST 表可以维护的是符合结合律可重复贡献的运算,即对于二元运算 ∗ * ,有 ( x ∗ y ) ∗ z = x ∗ ( y ∗ z ) (x*y)*z=x*(y*z) (xy)z=x(yz) 以及 x ∗ x = x x*x=x xx=x,比如最大值、最小值、最大公因数、最小公倍数、按位或、按位与。

洛谷 P2471 [SCOI2007] 降雨量

维护时间区间内降雨量最大值,关键在于判断,用线段树做也行。

记得离散化。

树状数组

运用二进制高效维护区间操作,通用性略差于线段树,但比线段树简洁,常数小于线段树。

当然大部分树状数组题都能用线段树做,卡常的除外。

洛谷 P3372 【模板】线段树 1

通过差分和维护多个信息可以实现区间修改和区间查询,此时可代替线段树。

Link

洛谷 P1908 逆序对

用权值树状数组维护逆序对的经典方法,记得离散化。

Link

洛谷 P1966 [NOIP2013 提高组] 火柴排队

逆序对的应用。

使得 ∑ ( a i − b i ) 2 \sum(a_i-b_i)^2 (aibi)2,就要使得 ∣ a i − b i ∣ |a_i-b_i| aibi 最小,即 a i a_i ai a a a 中的排名等于 b i b_i bi b b b 中的排名。

先将 a a a b b b 分别离散化。既然两盒火柴都可以交换,那么不妨只让 b b b 交换,则问题转化为将序列 b b b 转化为序列 a a a 的最小操作数。

建立一个序列 s s s s i s_i si b i b_i bi a a a 中的位置,则问题又转化为将 b b b 转化为递增序列的最小操作数,即为逆序对数。

洛谷 P5677 [GZOI2017]配对统计

离线处理。

a a a 排序,则能与 x x x 配对的只能为 x − 1 x-1 x1 x + 1 x+1 x+1,由此将所有好的配对预先处理出来,则每个配对可以视作一个区间,问题转化为可以被询问区间 [ L , R ] [L,R] [L,R] 完全覆盖的区间数量。

将所有配对和询问按右端点排序,依次处理询问,每次将右端点小于等于询问右端点的配对的左端点处加 1 1 1,答案为 [ 1 , R ] [1,R] [1,R] 的个数减去 [ 1 , L − 1 ] [1,L-1] [1,L1] 的个数。

用树状数组维护。

洛谷 P1972 [SDOI2009] HH的项链

类似于权值树状数组,询问按右端点排序,把 [ 1 , R ] [1,R] [1,R] 贝壳颜色标记上,答案为 [ 1 , R ] [1,R] [1,R] 的个数减去 [ 1 , L − 1 ] [1,L-1] [1,L1] 的个数。

洛谷 P4514 上帝造题的七分钟

二维树状数组模板。

用二维差分,则有

∑ i = 1 n ∑ j = 1 m a i j = ∑ i = 1 n ∑ j = 1 m ∑ x = 1 i ∑ y = 1 j d x y = ∑ i = 1 n ∑ j = 1 m ( n − i + 1 ) × ( m − j + 1 ) × d i j = ∑ i = 1 n ∑ j = 1 m [ ( n + 1 ) × ( m + 1 ) × d i j − ( n + 1 ) × j × d i j − ( m + 1 ) × i × d i j + i × j × d i j ] \begin{aligned} \sum\limits_{i=1}^n\sum\limits_{j=1}^ma_{ij} &=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x=1}^i\sum\limits_{y=1}^jd_{xy}\\ &=\sum\limits_{i=1}^n\sum\limits_{j=1}^m(n-i+1)\times(m-j+1)\times d_{ij}\\ &=\sum\limits_{i=1}^n\sum\limits_{j=1}^m[(n+1)\times(m+1)\times d_{ij}-(n+1)\times j\times d_{ij}-(m+1)\times i\times d_{ij}+i\times j\times d_{ij}] \end{aligned} i=1nj=1maij=i=1nj=1mx=1iy=1jdxy=i=1nj=1m(ni+1)×(mj+1)×dij=i=1nj=1m[(n+1)×(m+1)×dij(n+1)×j×dij(m+1)×i×dij+i×j×dij]

树状数组维护 d i j d_{ij} dij i × d i j i\times d_{ij} i×dij j × d i j j\times d_{ij} j×dij i × j × d i j i\times j\times d_{ij} i×j×dij

线段树

OI中最有用的数据结构,没有之一。

洛谷 P3373 【模板】线段树 2

关键就是 push_down 先乘后加。

洛谷 P4513 小白逛公园

线段树入门必做题,带修区间最大子段和。

线段树维护区间和、区间最大子段和、区间最大前缀和、区间最大后缀和四个信息。

关键是 push_up,区间最大子段和有三种情况:全在左儿子的区间、全在右儿子的区间、跨过两个区间,区间最大前缀和有两种情况:全在左儿子的区间、整个左儿子区间和部分右儿子区间,区间最大后缀和同理。因此可以通过上述四个信息维护区间最大子段和。

洛谷 P4588 [TJOI2018]数学计算

以时间作为线段树维护的区间,则答案即为区间乘,操作 2 2 2 相当于把 p o s pos pos 位置改为 1 1 1

洛谷 P4198 楼房重建

通过坐标算出斜率,则问题转化为维护从第一项开始的递增序列长度。

关键是 push_up,显然左儿子对应区间一定算入答案,只需考虑右儿子贡献。

计算一段区间在区间前最大值为 M M M 情况下对答案的贡献,存在两种情况:

  1. 该区间的左半区间最大值小于等于 M M M,则左半区间不能产生任何贡献,只需计算右半区间的贡献
  2. 左半区间最大值大于 M M M,则右半区间贡献为该区间的递增序列长度减去左区间对该区间的贡献,只需计算左半区间的贡献

由于每次只需计算半个区间的贡献,最多递归 log ⁡ n \log n logn 次,则 push_up 时间复杂度为 O ( log ⁡ 2 n ) O(\log^2 n) O(log2n)

线段树除答案外,额外维护区间最大值即可。

洛谷 P1502 窗口的星星

考虑一维的情况,类似滑动窗口,单调队列能做,权值线段树也能做,就是把以一颗星星的坐标为左端点,窗口大小为长度的区间加上亮度,答案就是全局最大值。

二维的话其中用单调队列维护其中一维(假设是宽)能够被算上的星星,另一维(假设是高)用权值线段树维护。具体操作是权值线段树维护 y y y 轴上大小为 H H H 的窗口,加入一颗新星星时把左端点为纵坐标,长度为 H H H 的区间加上亮度,单调队列维护 x x x 轴上大小为 W W W 的窗口,每次把新加入窗口的星星加到线段树中,退出窗口星星从线段树中减去(即加上负亮度)。

洛谷 P5076 【深基16.例7】普通二叉树(简化版)& P3369 【模板】普通平衡树

权值线段树有时候能当平衡树用。

数列分块

暴力数据结构。

LOJ #6277. 数列分块入门 1 & #6280. 数列分块入门 4

修改:整块加 tag,零块暴力加。

查询:整块取 sum,零块暴力算。

LOJ #6278. 数列分块入门 2 & #6279. 数列分块入门 3

修改:整块加 tag,零块暴力加然后排序。

查询:整块二分,零块暴力,记得算上 tag。

LOJ #6281. 数列分块入门 5 & 洛谷 P4145 上帝造题的七分钟 2 / 花神游历各国

因为数列的值相当有限,所以经过常数级次数就可以开到 0 0 0 1 1 1

开方:全都开到 0 0 0 1 1 1 的块打标记,标记过的不处理,没标记的暴力开。

查询:整块取 sum,零块暴力算。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值